您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Simple webm video generation script for https://dapiya.top/satellite/floater/
// ==UserScript== // @name Dapiya Floater Video Generator // @namespace http://tampermonkey.net/ // @version 0.1.1 // @description Simple webm video generation script for https://dapiya.top/satellite/floater/ // @author Dapiya // @match *://*.dapiya.top/satellite/floater/* // @grant none // @require https://unpkg.com/@ffmpeg/[email protected]/dist/ffmpeg.min.js // @license MIT // ==/UserScript== (async function() { 'use strict'; function addMainPageMP4Button() { if (!document.getElementById('MainCreateMP4Button')) { const mp4Button = document.createElement('button'); mp4Button.innerText = 'Create MP4 from Images'; mp4Button.className = 'btn btn-primary'; mp4Button.id = 'MainCreateMP4Button'; mp4Button.style.margin = '20px'; mp4Button.style.position = 'fixed'; mp4Button.style.top = '20px'; mp4Button.style.right = '20px'; mp4Button.addEventListener('click', async () => { console.log('MP4 button clicked, showing options dialog...'); const imageInfo = getImageInfo(); if (imageInfo) { console.log('Image info:', imageInfo); const options = await showOptionsDialog(); if (options) { await generateMP4FromImageInfo(imageInfo, options); } } else { alert('Could not determine storm name and data type. Please ensure the image is loaded on the page.'); } }); document.body.appendChild(mp4Button); } } function getImageInfo() { const imgElement = document.querySelector('img[src*="_"][src$=".png"]:not([src*="Dapiya"])'); console.log('Found image element:', imgElement); if (imgElement) { const srcParts = imgElement.src.split('/').pop().split('.'); console.log('Image source parts:', srcParts); if (srcParts.length >= 2) { const [nameAndType] = srcParts; const [stormName, dataType] = nameAndType.split('_'); if (stormName && dataType) { return { stormName: stormName, dataType: dataType }; } } } console.error('Could not determine storm name and data type.'); return null; } function showOptionsDialog() { return new Promise((resolve) => { const dialog = document.createElement('div'); dialog.style.position = 'fixed'; dialog.style.left = '50%'; dialog.style.top = '50%'; dialog.style.transform = 'translate(-50%, -50%)'; dialog.style.backgroundColor = 'white'; dialog.style.padding = '20px'; dialog.style.border = '1px solid black'; dialog.style.zIndex = '1000'; dialog.innerHTML = ` <h2>Video Generation Options</h2> <label>Number of images: <input type="number" id="numImages" value="20" min="1" max="2000"></label><br><br> <label>Frame rate: <input type="number" id="frameRate" value="5" min="1" max="60"></label><br><br> <label>Bitrate (kbps): <input type="number" id="bitrate" value="2000" min="500" max="16000"></label><br><br> <button id="generateButton">Generate Video</button> <button id="cancelButton">Cancel</button> `; document.body.appendChild(dialog); document.getElementById('generateButton').addEventListener('click', () => { const numImages = parseInt(document.getElementById('numImages').value); const frameRate = parseInt(document.getElementById('frameRate').value); const bitrate = parseInt(document.getElementById('bitrate').value) * 1000; // Convert to bps document.body.removeChild(dialog); resolve({ numImages, frameRate, bitrate }); }); document.getElementById('cancelButton').addEventListener('click', () => { document.body.removeChild(dialog); resolve(null); }); }); } async function getLatestImageUrls(stormName, dataType, numImages) { console.log(`Fetching image URLs for ${stormName} ${dataType}`); const baseUrl = `https://data.dapiya.top/history/${stormName}/${dataType}/`; const response = await fetch(baseUrl); const text = await response.text(); const parser = new DOMParser(); const doc = parser.parseFromString(text, 'text/html'); const links = Array.from(doc.querySelectorAll('a')) .map(a => a.href) .filter(href => href.endsWith('.png') && href.includes(`${stormName}_${dataType}`)) .sort((a, b) => b.localeCompare(a)) // Sort in descending order .slice(0, numImages) // Take the first 'numImages' (which are the latest due to sorting) .reverse() // Reverse to get chronological order .map(href => `${baseUrl}${href.split('/').pop()}`); console.log('Found image URLs:', links); return links; } async function getImageDimensions(url) { return new Promise((resolve, reject) => { const img = new Image(); img.onload = () => resolve({ width: img.width, height: img.height }); img.onerror = reject; img.src = url; }); } async function generateMP4FromImageInfo(imageInfo, options) { try { const imageUrls = await getLatestImageUrls(imageInfo.stormName, imageInfo.dataType, options.numImages); console.log('Image URLs:', imageUrls); if (imageUrls.length === 0) { console.error('No image URLs found'); return; } const dimensions = await getImageDimensions(imageUrls[0]); console.log('Image dimensions:', dimensions); const images = await Promise.all(imageUrls.map(async url => { const response = await fetch(url); const blob = await response.blob(); return URL.createObjectURL(blob); })); console.log('Loaded images:', images); const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); canvas.width = dimensions.width; canvas.height = dimensions.height; const stream = canvas.captureStream(options.frameRate); const mediaRecorder = new MediaRecorder(stream, { mimeType: 'video/webm', videoBitsPerSecond: options.bitrate }); const chunks = []; mediaRecorder.ondataavailable = e => chunks.push(e.data); mediaRecorder.onstop = async () => { const webmBlob = new Blob(chunks, { type: 'video/webm' }); const url = URL.createObjectURL(webmBlob); const a = document.createElement('a'); a.href = url; a.download = `${imageInfo.stormName}_${imageInfo.dataType}_video.webm`; document.body.appendChild(a); a.click(); document.body.removeChild(a); console.log('WebM video generated and download triggered.'); }; mediaRecorder.start(); for (let i = 0; i < images.length; i++) { await new Promise((resolve) => { const img = new Image(); img.onload = () => { ctx.drawImage(img, 0, 0, canvas.width, canvas.height); if (i === images.length - 1) { mediaRecorder.stop(); } setTimeout(resolve, 1000 / options.frameRate); }; img.src = images[i]; }); } } catch (error) { console.error('Error generating video from images:', error); console.error('Error stack:', error.stack); } } addMainPageMP4Button(); })();