Extract images from Twitter media and play slideshow with controls and countdown timer
当前为
// ==UserScript==
// @name Twitter Media Gallery Slideshow with Timer
// @namespace http://tampermonkey.net/
// @version 1.4
// @description Extract images from Twitter media and play slideshow with controls and countdown timer
// @author pshot
// @match https://twitter.com/*
// @match https://x.com/*
// @grant none
// @license MIT
// ==/UserScript==
(function () {
'use strict';
let slideshowOverlay = null;
let slideshowImages = [];
let currentIndex = 0;
let slideshowInterval = null;
let countdownInterval = null;
let countdownSeconds = 10;
// Extract full-size image URLs from Twitter media gallery
function extractImageUrls() {
const seen = new Set();
const images = [];
const imgElements = document.querySelectorAll('img[src*="pbs.twimg.com/media/"]');
imgElements.forEach(img => {
const src = img.src;
const baseUrl = src.split('?')[0];
if (!seen.has(baseUrl)) {
seen.add(baseUrl);
const fullUrl = baseUrl + '?format=jpg&name=large';
images.push(fullUrl);
}
});
return images;
}
// Create and show slideshow overlay
function showSlideshow(images) {
if (images.length === 0) {
alert('No images found!');
return;
}
if (slideshowOverlay) {
slideshowOverlay.remove();
clearInterval(slideshowInterval);
clearInterval(countdownInterval);
}
slideshowImages = images;
currentIndex = 0;
countdownSeconds = 10;
slideshowOverlay = document.createElement('div');
slideshowOverlay.style.position = 'fixed';
slideshowOverlay.style.top = '0';
slideshowOverlay.style.left = '0';
slideshowOverlay.style.width = '100vw';
slideshowOverlay.style.height = '100vh';
slideshowOverlay.style.backgroundColor = 'rgba(0,0,0,0.9)';
slideshowOverlay.style.display = 'flex';
slideshowOverlay.style.alignItems = 'center';
slideshowOverlay.style.justifyContent = 'center';
slideshowOverlay.style.flexDirection = 'column';
slideshowOverlay.style.zIndex = '100000';
// Image element
const img = document.createElement('img');
img.src = slideshowImages[currentIndex];
img.style.maxWidth = '90%';
img.style.maxHeight = '90%';
img.style.borderRadius = '10px';
img.style.boxShadow = '0 0 20px rgba(255, 255, 255, 0.7)';
slideshowOverlay.appendChild(img);
// Countdown timer display
const timerDisplay = document.createElement('div');
timerDisplay.style.color = 'white';
timerDisplay.style.fontSize = '18px';
timerDisplay.style.marginTop = '10px';
timerDisplay.style.fontFamily = 'Arial, sans-serif';
timerDisplay.textContent = `Next image in: ${countdownSeconds} seconds`;
slideshowOverlay.appendChild(timerDisplay);
// Close button
const closeBtn = document.createElement('button');
closeBtn.innerText = '✕';
closeBtn.style.position = 'fixed';
closeBtn.style.top = '20px';
closeBtn.style.right = '20px';
closeBtn.style.fontSize = '30px';
closeBtn.style.background = 'transparent';
closeBtn.style.color = 'white';
closeBtn.style.border = 'none';
closeBtn.style.cursor = 'pointer';
closeBtn.style.zIndex = '100001';
closeBtn.title = 'Close Slideshow';
closeBtn.addEventListener('click', () => {
clearInterval(slideshowInterval);
clearInterval(countdownInterval);
slideshowOverlay.remove();
slideshowOverlay = null;
});
slideshowOverlay.appendChild(closeBtn);
// Navigation functions
function showImage(index) {
currentIndex = (index + slideshowImages.length) % slideshowImages.length;
img.src = slideshowImages[currentIndex];
}
function nextImage() {
showImage(currentIndex + 1);
countdownSeconds = 10;
timerDisplay.textContent = `Next image in: ${countdownSeconds} seconds`;
}
function prevImage() {
showImage(currentIndex - 1);
countdownSeconds = 10;
timerDisplay.textContent = `Next image in: ${countdownSeconds} seconds`;
}
// Keyboard controls
function keyHandler(e) {
if (!slideshowOverlay) return;
if (e.key === 'ArrowRight') {
nextImage();
resetInterval();
} else if (e.key === 'ArrowLeft') {
prevImage();
resetInterval();
} else if (e.key === 'Escape') {
closeBtn.click();
}
}
document.addEventListener('keydown', keyHandler);
// Auto-advance every 10 seconds
function resetInterval() {
clearInterval(slideshowInterval);
clearInterval(countdownInterval);
countdownSeconds = 10;
timerDisplay.textContent = `Next image in: ${countdownSeconds} seconds`;
slideshowInterval = setInterval(nextImage, 10000);
countdownInterval = setInterval(() => {
countdownSeconds--;
if (countdownSeconds < 0) countdownSeconds = 0;
timerDisplay.textContent = `Next image in: ${countdownSeconds} seconds`;
}, 1000);
}
resetInterval();
document.body.appendChild(slideshowOverlay);
}
// Add control button to page
function addButton() {
if (document.getElementById('extract-images-btn')) return;
const btn = document.createElement('button');
btn.innerText = 'Start Twitter Slideshow';
btn.id = 'extract-images-btn';
btn.style.position = 'fixed';
btn.style.top = '10px';
btn.style.right = '10px';
btn.style.zIndex = '9999';
btn.style.padding = '10px';
btn.style.background = '#1da1f2';
btn.style.color = 'white';
btn.style.border = 'none';
btn.style.borderRadius = '5px';
btn.style.cursor = 'pointer';
btn.title = 'Extract images and start slideshow';
btn.addEventListener('click', () => {
const images = extractImageUrls();
showSlideshow(images);
});
document.body.appendChild(btn);
}
// Observe DOM to add button dynamically (Twitter SPA)
const observer = new MutationObserver(() => {
addButton();
});
observer.observe(document.body, { childList: true, subtree: true });
// Initial call
addButton();
})();