maydayjeffk's picture
Add 1 files
e532a92 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>PUNK IMAGE GRABBER - Stick It To The Man</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
@import url('https://fonts.googleapis.com/css2?family=Rubik+Mono+One&family=VT323&display=swap');
body {
background-color: #000;
font-family: 'VT323', monospace;
color: #fff;
overflow-x: hidden;
}
.title {
font-family: 'Rubik Mono One', sans-serif;
text-shadow: 3px 3px 0 #f00, -3px -3px 0 #00f;
}
.graffiti {
position: absolute;
transform: rotate(-5deg);
color: #ff0;
font-size: 1.2rem;
text-shadow: 2px 2px 0 #000;
z-index: 100;
}
.graffiti-1 {
top: 15%;
left: 5%;
}
.graffiti-2 {
top: 65%;
right: 5%;
}
.graffiti-3 {
bottom: 10%;
left: 10%;
}
.btn-punk {
background: linear-gradient(45deg, #f00, #00f);
border: 2px solid #fff;
color: white;
font-weight: bold;
transition: all 0.3s;
}
.btn-punk:hover {
transform: scale(1.05);
box-shadow: 0 0 15px #f00;
}
.carousel {
background-color: rgba(0, 0, 0, 0.9);
border: 3px solid #f00;
}
.image-tile {
border: 2px solid #fff;
transition: all 0.3s;
}
.image-tile:hover {
border-color: #f00;
transform: scale(1.02);
}
.selected {
border: 3px solid #00f;
box-shadow: 0 0 15px #00f;
}
.progress-bar {
height: 10px;
background-color: #333;
}
.progress-fill {
height: 100%;
background: linear-gradient(90deg, #f00, #00f);
transition: width 0.3s;
}
.filetype-badge {
background-color: #333;
border: 1px solid #fff;
}
.convert-option {
border: 1px solid #fff;
transition: all 0.3s;
}
.convert-option:hover {
background-color: #333;
border-color: #f00;
}
.convert-option.selected-format {
background-color: #00f;
color: #fff;
}
.spray-paint {
position: absolute;
width: 100%;
height: 100%;
pointer-events: none;
z-index: -1;
opacity: 0.1;
}
/* CORS proxy warning */
.cors-warning {
background-color: #f00;
color: #fff;
padding: 10px;
margin-top: 10px;
border: 2px solid #fff;
font-size: 14px;
}
</style>
</head>
<body>
<!-- Spray paint background effect -->
<div class="spray-paint">
<div style="position: absolute; top: 20%; left: 10%; width: 200px; height: 150px; background-color: #f00; transform: rotate(-15deg);"></div>
<div style="position: absolute; top: 60%; right: 15%; width: 180px; height: 120px; background-color: #00f; transform: rotate(10deg);"></div>
<div style="position: absolute; bottom: 10%; left: 50%; width: 150px; height: 100px; background-color: #ff0; transform: rotate(-5deg);"></div>
</div>
<!-- Graffiti tags -->
<div class="graffiti graffiti-1">FUCK CAPITALISM</div>
<div class="graffiti graffiti-2">TRUMP IS A FASCIST</div>
<div class="graffiti graffiti-3">EAT THE RICH</div>
<div class="container mx-auto px-4 py-8">
<header class="text-center mb-8">
<h1 class="title text-5xl md:text-6xl mb-4">PUNK IMAGE GRABBER</h1>
<p class="text-xl">Steal images like you steal from the bourgeoisie</p>
</header>
<div class="max-w-3xl mx-auto bg-gray-900 p-6 rounded-lg border-2 border-red-500 mb-8 relative">
<h2 class="text-2xl mb-4">ENTER URL TO SCRAPE</h2>
<div class="flex">
<input type="text" id="urlInput" placeholder="https://example.com" class="flex-grow px-4 py-2 bg-black text-white border-2 border-white focus:border-red-500 outline-none">
<button id="scrapeBtn" class="btn-punk px-6 py-2 ml-2">
<i class="fas fa-skull-crossbones mr-2"></i>GRAB 'EM
</button>
</div>
<p class="text-sm mt-2 text-gray-400">Works with JPG, PNG, GIF, WEBP, SVG, BMP, TIFF, and more</p>
<div class="cors-warning hidden" id="corsWarning">
WARNING: Due to CORS restrictions, we're using a proxy to scrape this site. Some images may not load properly. For best results, use the browser extension version.
</div>
<div class="mt-6">
<h3 class="text-xl mb-2">OPTIONS</h3>
<div class="flex flex-wrap gap-4">
<label class="flex items-center">
<input type="checkbox" id="recursiveCheck" class="mr-2">
<span>Recursive scraping (Deeper = more dangerous)</span>
</label>
<label class="flex items-center">
<input type="checkbox" id="filterCheck" class="mr-2" checked>
<span>Filter out tiny images</span>
</label>
</div>
</div>
<div class="mt-6 hidden" id="progressContainer">
<div class="flex justify-between mb-2">
<span>Scraping progress...</span>
<span id="progressText">0%</span>
</div>
<div class="progress-bar">
<div id="progressFill" class="progress-fill" style="width: 0%"></div>
</div>
<div class="mt-2 text-sm" id="statusText">Waiting to start...</div>
</div>
</div>
<div class="hidden" id="resultsSection">
<div class="flex justify-between items-center mb-4">
<h2 class="text-3xl">YOUR STOLEN GOODS</h2>
<div>
<button id="selectAllBtn" class="btn-punk px-4 py-1 mr-2">
<i class="fas fa-check-double mr-1"></i>SELECT ALL
</button>
<button id="convertBtn" class="btn-punk px-4 py-1 mr-2">
<i class="fas fa-exchange-alt mr-1"></i>CONVERT
</button>
<button id="downloadBtn" class="btn-punk px-4 py-1 mr-2">
<i class="fas fa-download mr-1"></i>DOWNLOAD
</button>
<button id="deleteBtn" class="btn-punk bg-red-900 px-4 py-1">
<i class="fas fa-trash mr-1"></i>DELETE
</button>
</div>
</div>
<div id="imageGrid" class="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 gap-4 mb-8">
<!-- Images will be loaded here -->
</div>
</div>
</div>
<!-- Image Carousel Modal -->
<div id="carouselModal" class="fixed inset-0 bg-black bg-opacity-90 flex items-center justify-center z-50 hidden">
<div class="carousel w-full max-w-4xl p-4 relative">
<button id="closeCarousel" class="absolute top-2 right-2 text-white text-2xl z-50">&times;</button>
<div class="relative h-96 overflow-hidden mb-4">
<img id="carouselImage" src="" alt="" class="w-full h-full object-contain">
</div>
<div class="flex justify-between items-center">
<button id="prevBtn" class="btn-punk px-4 py-1">
<i class="fas fa-arrow-left mr-1"></i>PREV
</button>
<span id="imageCounter" class="text-xl">1/1</span>
<button id="nextBtn" class="btn-punk px-4 py-1">
NEXT <i class="fas fa-arrow-right ml-1"></i>
</button>
</div>
</div>
</div>
<!-- Convert Modal -->
<div id="convertModal" class="fixed inset-0 bg-black bg-opacity-90 flex items-center justify-center z-50 hidden">
<div class="carousel w-full max-w-2xl p-6 relative">
<button id="closeConvert" class="absolute top-2 right-2 text-white text-2xl z-50">&times;</button>
<h2 class="text-3xl mb-6">CONVERT TO WHAT?</h2>
<div class="grid grid-cols-2 sm:grid-cols-3 gap-4 mb-8">
<div class="convert-option p-4 text-center cursor-pointer" data-format="jpg">
<i class="fas fa-file-image text-4xl mb-2"></i>
<div>JPG</div>
</div>
<div class="convert-option p-4 text-center cursor-pointer" data-format="png">
<i class="fas fa-file-image text-4xl mb-2"></i>
<div>PNG</div>
</div>
<div class="convert-option p-4 text-center cursor-pointer" data-format="webp">
<i class="fas fa-file-image text-4xl mb-2"></i>
<div>WEBP</div>
</div>
<div class="convert-option p-4 text-center cursor-pointer" data-format="gif">
<i class="fas fa-file-image text-4xl mb-2"></i>
<div>GIF</div>
</div>
<div class="convert-option p-4 text-center cursor-pointer" data-format="bmp">
<i class="fas fa-file-image text-4xl mb-2"></i>
<div>BMP</div>
</div>
<div class="convert-option p-4 text-center cursor-pointer" data-format="ico">
<i class="fas fa-file-image text-4xl mb-2"></i>
<div>ICO</div>
</div>
</div>
<div class="flex justify-center">
<button id="confirmConvertBtn" class="btn-punk px-6 py-2">
<i class="fas fa-magic mr-2"></i>CONVERT THESE BITCHES
</button>
</div>
</div>
</div>
<!-- Delete Confirmation Modal -->
<div id="deleteModal" class="fixed inset-0 bg-black bg-opacity-90 flex items-center justify-center z-50 hidden">
<div class="carousel w-full max-w-md p-6 relative">
<h2 class="text-3xl mb-6 text-center">DESTROY THE EVIDENCE?</h2>
<p class="text-xl mb-8 text-center">This will delete <span id="deleteCount">0</span> selected images permanently.</p>
<div class="flex justify-center gap-4">
<button id="cancelDelete" class="btn-punk px-6 py-2">
<i class="fas fa-times mr-2"></i>NO, KEEP 'EM
</button>
<button id="confirmDelete" class="btn-punk bg-red-900 px-6 py-2">
<i class="fas fa-trash mr-2"></i>BURN IT ALL
</button>
</div>
</div>
</div>
<script>
// Global variables
let scrapedImages = [];
let currentCarouselIndex = 0;
let selectedImages = [];
let selectedFormat = null;
// DOM elements
const urlInput = document.getElementById('urlInput');
const scrapeBtn = document.getElementById('scrapeBtn');
const progressContainer = document.getElementById('progressContainer');
const progressFill = document.getElementById('progressFill');
const progressText = document.getElementById('progressText');
const statusText = document.getElementById('statusText');
const resultsSection = document.getElementById('resultsSection');
const imageGrid = document.getElementById('imageGrid');
const carouselModal = document.getElementById('carouselModal');
const carouselImage = document.getElementById('carouselImage');
const imageCounter = document.getElementById('imageCounter');
const closeCarousel = document.getElementById('closeCarousel');
const prevBtn = document.getElementById('prevBtn');
const nextBtn = document.getElementById('nextBtn');
const selectAllBtn = document.getElementById('selectAllBtn');
const downloadBtn = document.getElementById('downloadBtn');
const deleteBtn = document.getElementById('deleteBtn');
const convertBtn = document.getElementById('convertBtn');
const convertModal = document.getElementById('convertModal');
const closeConvert = document.getElementById('closeConvert');
const confirmConvertBtn = document.getElementById('confirmConvertBtn');
const deleteModal = document.getElementById('deleteModal');
const deleteCount = document.getElementById('deleteCount');
const cancelDelete = document.getElementById('cancelDelete');
const confirmDelete = document.getElementById('confirmDelete');
const recursiveCheck = document.getElementById('recursiveCheck');
const filterCheck = document.getElementById('filterCheck');
const corsWarning = document.getElementById('corsWarning');
// Event listeners
scrapeBtn.addEventListener('click', startScraping);
closeCarousel.addEventListener('click', () => carouselModal.classList.add('hidden'));
prevBtn.addEventListener('click', showPrevImage);
nextBtn.addEventListener('click', showNextImage);
selectAllBtn.addEventListener('click', toggleSelectAll);
downloadBtn.addEventListener('click', downloadSelected);
deleteBtn.addEventListener('click', showDeleteConfirmation);
convertBtn.addEventListener('click', () => convertModal.classList.remove('hidden'));
closeConvert.addEventListener('click', () => convertModal.classList.add('hidden'));
confirmConvertBtn.addEventListener('click', convertImages);
cancelDelete.addEventListener('click', () => deleteModal.classList.add('hidden'));
confirmDelete.addEventListener('click', deleteSelected);
// Add event listeners to convert options
document.querySelectorAll('.convert-option').forEach(option => {
option.addEventListener('click', function() {
document.querySelectorAll('.convert-option').forEach(opt => {
opt.classList.remove('selected-format');
});
this.classList.add('selected-format');
selectedFormat = this.dataset.format;
});
});
// Functions
function startScraping() {
const url = urlInput.value.trim();
if (!url) {
alert("ENTER A URL, YOU REBEL!");
return;
}
// Reset state
scrapedImages = [];
selectedImages = [];
imageGrid.innerHTML = '';
// Show progress
progressContainer.classList.remove('hidden');
progressFill.style.width = '0%';
progressText.textContent = '0%';
statusText.textContent = 'Starting the digital heist...';
// Check if URL is from same origin
try {
const urlObj = new URL(url);
const currentOrigin = new URL(window.location.href).origin;
if (urlObj.origin === currentOrigin) {
// Same origin - we can scrape directly
corsWarning.classList.add('hidden');
scrapeSameOrigin(url);
} else {
// Different origin - need to use proxy
corsWarning.classList.remove('hidden');
scrapeWithProxy(url);
}
} catch (e) {
alert("INVALID URL! TRY AGAIN, COMRADE!");
progressContainer.classList.add('hidden');
}
}
function scrapeSameOrigin(url) {
fetch(url)
.then(response => response.text())
.then(html => {
// Parse HTML and extract images
const parser = new DOMParser();
const doc = parser.parseFromString(html, 'text/html');
const images = doc.querySelectorAll('img');
// Update progress
progressFill.style.width = '50%';
progressText.textContent = '50%';
statusText.textContent = 'Found ' + images.length + ' images...';
// Process images
processImages(Array.from(images), url);
})
.catch(error => {
console.error('Error:', error);
statusText.textContent = 'Failed to scrape: ' + error.message;
setTimeout(() => {
progressContainer.classList.add('hidden');
}, 2000);
});
}
function scrapeWithProxy(url) {
// Using a CORS proxy - note: in production you'd want your own proxy server
const proxyUrl = `https://api.allorigins.win/get?url=${encodeURIComponent(url)}`;
fetch(proxyUrl)
.then(response => response.json())
.then(data => {
if (data.contents) {
// Parse HTML and extract images
const parser = new DOMParser();
const doc = parser.parseFromString(data.contents, 'text/html');
const images = doc.querySelectorAll('img');
// Update progress
progressFill.style.width = '50%';
progressText.textContent = '50%';
statusText.textContent = 'Found ' + images.length + ' images...';
// Process images
processImages(Array.from(images), url);
} else {
throw new Error('Failed to get content through proxy');
}
})
.catch(error => {
console.error('Error:', error);
statusText.textContent = 'Failed to scrape: ' + error.message;
setTimeout(() => {
progressContainer.classList.add('hidden');
}, 2000);
});
}
function processImages(images, baseUrl) {
const baseUrlObj = new URL(baseUrl);
const filteredImages = [];
// Process each image
images.forEach((img, index) => {
let src = img.getAttribute('src') || '';
let width = img.width || 0;
let height = img.height || 0;
// Resolve relative URLs
if (src.startsWith('//')) {
src = baseUrlObj.protocol + src;
} else if (src.startsWith('/')) {
src = baseUrlObj.origin + src;
} else if (!src.startsWith('http')) {
src = new URL(src, baseUrl).href;
}
// Get file extension
let format = 'unknown';
const match = src.match(/\.(jpe?g|png|gif|webp|svg|bmp|tiff?|ico)/i);
if (match) {
format = match[1].toLowerCase();
}
// Check if we should filter small images
const shouldFilter = filterCheck.checked;
if (!shouldFilter || (width > 100 && height > 100)) {
filteredImages.push({
url: src,
format: format,
width: width,
height: height,
originalUrl: src
});
}
});
// Update progress
progressFill.style.width = '80%';
progressText.textContent = '80%';
statusText.textContent = 'Processing ' + filteredImages.length + ' images...';
// Verify which images are accessible
verifyImageAccess(filteredImages);
}
function verifyImageAccess(images) {
let verifiedCount = 0;
const verifiedImages = [];
// Function to check image access
function checkImage(index) {
if (index >= images.length) {
// All images checked
scrapedImages = verifiedImages;
finishScraping();
return;
}
const img = images[index];
const testImg = new Image();
testImg.onload = function() {
// Image is accessible
verifiedImages.push(img);
verifiedCount++;
// Update progress
const progress = 80 + (20 * verifiedCount / images.length);
progressFill.style.width = progress + '%';
progressText.textContent = Math.floor(progress) + '%';
statusText.textContent = `Verified ${verifiedCount}/${images.length} images...`;
// Check next image
checkImage(index + 1);
};
testImg.onerror = function() {
// Image not accessible, skip it
verifiedCount++;
// Update progress
const progress = 80 + (20 * verifiedCount / images.length);
progressFill.style.width = progress + '%';
progressText.textContent = Math.floor(progress) + '%';
statusText.textContent = `Verified ${verifiedCount}/${images.length} images...`;
// Check next image
checkImage(index + 1);
};
testImg.src = img.url;
}
// Start checking images
checkImage(0);
}
function finishScraping() {
statusText.textContent = 'Done! Found ' + scrapedImages.length + ' accessible images.';
// Display images
displayImages(scrapedImages);
// Show results section
resultsSection.classList.remove('hidden');
// Hide progress after delay
setTimeout(() => {
progressContainer.classList.add('hidden');
}, 2000);
}
function displayImages(images) {
imageGrid.innerHTML = '';
images.forEach((img, index) => {
const tile = document.createElement('div');
tile.className = 'image-tile relative group cursor-pointer';
tile.dataset.index = index;
tile.innerHTML = `
<div class="relative pb-full">
<img src="${img.url}" alt="Scraped image" class="absolute h-full w-full object-cover" loading="lazy" onerror="this.parentElement.parentElement.remove()">
</div>
<div class="absolute bottom-0 left-0 right-0 bg-black bg-opacity-70 p-2 hidden group-hover:block">
<span class="filetype-badge px-2 py-1 text-xs">${img.format.toUpperCase()}</span>
<span class="text-xs ml-2">${img.width}x${img.height}</span>
</div>
<div class="absolute top-2 right-2 hidden group-hover:block">
<button class="select-btn bg-blue-500 text-white rounded-full w-6 h-6 flex items-center justify-center">
<i class="fas fa-check text-xs"></i>
</button>
</div>
`;
tile.addEventListener('click', () => {
currentCarouselIndex = index;
showImageInCarousel();
});
// Add select button event
const selectBtn = tile.querySelector('.select-btn');
selectBtn.addEventListener('click', (e) => {
e.stopPropagation();
toggleImageSelection(index, tile);
});
imageGrid.appendChild(tile);
});
}
function showImageInCarousel() {
if (scrapedImages.length === 0) return;
const img = scrapedImages[currentCarouselIndex];
carouselImage.src = img.url;
imageCounter.textContent = `${currentCarouselIndex + 1}/${scrapedImages.length}`;
carouselModal.classList.remove('hidden');
}
function showPrevImage() {
if (currentCarouselIndex > 0) {
currentCarouselIndex--;
} else {
currentCarouselIndex = scrapedImages.length - 1;
}
showImageInCarousel();
}
function showNextImage() {
if (currentCarouselIndex < scrapedImages.length - 1) {
currentCarouselIndex++;
} else {
currentCarouselIndex = 0;
}
showImageInCarousel();
}
function toggleImageSelection(index, tile = null) {
if (!tile) {
tile = document.querySelector(`.image-tile[data-index="${index}"]`);
}
const selectedIndex = selectedImages.indexOf(index);
if (selectedIndex === -1) {
selectedImages.push(index);
tile.classList.add('selected');
} else {
selectedImages.splice(selectedIndex, 1);
tile.classList.remove('selected');
}
updateSelectionCount();
}
function toggleSelectAll() {
const allSelected = selectedImages.length === scrapedImages.length;
if (allSelected) {
// Deselect all
selectedImages = [];
document.querySelectorAll('.image-tile').forEach(tile => {
tile.classList.remove('selected');
});
} else {
// Select all
selectedImages = Array.from({ length: scrapedImages.length }, (_, i) => i);
document.querySelectorAll('.image-tile').forEach(tile => {
tile.classList.add('selected');
});
}
updateSelectionCount();
}
function updateSelectionCount() {
const count = selectedImages.length;
if (count > 0) {
selectAllBtn.innerHTML = `<i class="fas fa-check-double mr-1"></i>${count === scrapedImages.length ? 'DESELECT ALL' : 'SELECT ALL'}`;
} else {
selectAllBtn.innerHTML = '<i class="fas fa-check-double mr-1"></i>SELECT ALL';
}
}
function downloadSelected() {
if (selectedImages.length === 0) {
alert("SELECT SOME IMAGES FIRST, YOU ANARCHIST!");
return;
}
// Create a zip file of selected images
alert(`Preparing to download ${selectedImages.length} images...\n\n(In a real app, this would create a ZIP file)`);
// For demo purposes, we'll download each image individually
selectedImages.forEach(index => {
const img = scrapedImages[index];
const a = document.createElement('a');
a.href = img.url;
a.download = `stolen-image-${index}.${img.format}`;
a.target = '_blank';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
});
}
function showDeleteConfirmation() {
if (selectedImages.length === 0) {
alert("SELECT SOME IMAGES TO DELETE, YOU DESTRUCTIVE PUNK!");
return;
}
deleteCount.textContent = selectedImages.length;
deleteModal.classList.remove('hidden');
}
function deleteSelected() {
// Sort in descending order to avoid index issues when deleting
const sortedIndexes = [...selectedImages].sort((a, b) => b - a);
// Delete from scrapedImages
sortedIndexes.forEach(index => {
scrapedImages.splice(index, 1);
});
// Reset selection
selectedImages = [];
// Redisplay images
displayImages(scrapedImages);
// Close modal
deleteModal.classList.add('hidden');
// Show message
alert("Images deleted! The evidence is destroyed!");
}
function convertImages() {
if (selectedImages.length === 0) {
alert("SELECT SOME IMAGES TO CONVERT, YOU REBEL!");
return;
}
if (!selectedFormat) {
alert("CHOOSE A FORMAT, YOU ANARCHIST!");
return;
}
// In a real app, this would convert images to the selected format
alert(`Converting ${selectedImages.length} images to ${selectedFormat.toUpperCase()}...\n\n(In a real app, this would use a conversion API)`);
// Simulate conversion by changing the format in our data
selectedImages.forEach(index => {
scrapedImages[index].format = selectedFormat;
});
// Update the display
displayImages(scrapedImages);
// Show message
alert(`Conversion to ${selectedFormat.toUpperCase()} complete! Take that, capitalist image standards!`);
convertModal.classList.add('hidden');
}
// Close modals when clicking outside
window.addEventListener('click', (e) => {
if (e.target === carouselModal) {
carouselModal.classList.add('hidden');
}
if (e.target === convertModal) {
convertModal.classList.add('hidden');
}
if (e.target === deleteModal) {
deleteModal.classList.add('hidden');
}
});
</script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=maydayjeffk/batch-image-downloader-and-converter" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>