您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Adds a button to download all files in a Simfileshare folder as a zip file and individual download buttons
- // ==UserScript==
- // @name Simfileshare Folder Downloader
- // @namespace https://github.com/dear-clouds/mio-userscripts
- // @version 1.0
- // @description Adds a button to download all files in a Simfileshare folder as a zip file and individual download buttons
- // @author Mio.
- // @supportURL https://github.com/dear-clouds/mio-userscripts/issues
- // @icon https://www.google.com/s2/favicons?sz=64&domain=simfileshare.net
- // @match *://*.simfileshare.net/folder/*
- // @grant none
- // @license GPL-3.0
- // @require https://cdnjs.cloudflare.com/ajax/libs/jszip/3.7.1/jszip.min.js
- // @require https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2.0.5/FileSaver.min.js
- // ==/UserScript==
- (function() {
- 'use strict';
- window.addEventListener('load', () => {
- const MAX_CONCURRENT_DOWNLOADS = 8; // Control how many files to download concurrently here
- // console.log('Page loaded, initializing script...');
- // Button Download All
- const downloadAllButton = document.createElement('button');
- downloadAllButton.innerHTML = 'Download All Files as ZIP';
- const loaderSpanAll = document.createElement('span');
- loaderSpanAll.className = 'loader';
- loaderSpanAll.style.display = 'none';
- downloadAllButton.appendChild(loaderSpanAll);
- downloadAllButton.style.padding = '10px';
- downloadAllButton.style.backgroundColor = '#4CAF50';
- downloadAllButton.style.color = 'white';
- downloadAllButton.style.border = 'none';
- downloadAllButton.style.cursor = 'pointer';
- downloadAllButton.style.marginBottom = '10px';
- downloadAllButton.style.position = 'relative';
- const h4Element = document.querySelector('h4');
- if (h4Element) {
- h4Element.parentNode.insertBefore(downloadAllButton, h4Element.nextSibling);
- // console.log('Download All button appended successfully');
- }
- const loaderStyle = document.createElement('style');
- loaderStyle.innerHTML = `
- .loader {
- border: 4px solid #f3f3f3;
- border-top: 4px solid #3498db;
- border-radius: 50%;
- width: 16px;
- height: 16px;
- animation: spin 1s linear infinite;
- position: relative;
- margin-left: 10px;
- }
- @keyframes spin {
- 0% { transform: rotate(0deg); }
- 100% { transform: rotate(360deg); }
- }
- .dl-button {
- margin-left: 10px;
- padding: 4px 8px;
- background-color: #3498db;
- color: white;
- border: none;
- cursor: pointer;
- font-size: 0.9em;
- position: relative;
- }
- `;
- document.head.appendChild(loaderStyle);
- // console.log('Loader styles added');
- downloadAllButton.addEventListener('click', async () => {
- console.log('Download All button clicked');
- // Get all the download links in the folder
- const downloadLinks = document.querySelectorAll('a[href*="/download/"]');
- console.log(`Found ${downloadLinks.length} download links`);
- if (downloadLinks.length === 0) {
- alert('No files found to download!');
- return;
- }
- loaderSpanAll.style.display = 'inline-block';
- // console.log('Loader displayed for Download All button');
- // Create a ZIP file with JSZip
- const zip = new JSZip();
- const folderTitle = h4Element.textContent.replace('Folder: ', '').trim();
- console.log(`Folder title determined: ${folderTitle}`);
- // Function to fetch files with retries and concurrency limit
- const fetchFileWithRetry = async (url, retries = 20) => { // Here you can control the number of retries. Default: 20
- for (let attempt = 0; attempt < retries; attempt++) {
- try {
- const directUrl = url.replace('/download/', '/cdn/download/') + '/?dl';
- console.log(`Fetching: ${directUrl} (Attempt ${attempt + 1})`);
- const response = await fetch(directUrl);
- if (response.ok) {
- console.log(`Successfully fetched: ${directUrl}`);
- return await response.blob();
- } else {
- console.warn(`Attempt ${attempt + 1} failed: ${response.statusText}`);
- }
- } catch (error) {
- console.warn(`Attempt ${attempt + 1} failed: ${error.message}`);
- }
- await new Promise(resolve => setTimeout(resolve, 1000)); // Wait before retrying
- }
- throw new Error(`Failed to fetch file after ${retries} attempts: ${url}`);
- };
- // Concurrency limiting function
- const limitConcurrency = async (limit, tasks) => {
- const results = [];
- const executing = [];
- for (const task of tasks) {
- const p = task().then(result => {
- executing.splice(executing.indexOf(p), 1);
- return result;
- });
- results.push(p);
- executing.push(p);
- if (executing.length >= limit) {
- await Promise.race(executing);
- }
- }
- return Promise.all(results);
- };
- // Prepare a list of tasks to fetch each file
- const tasks = Array.from(downloadLinks).map(link => async () => {
- try {
- console.log(`Preparing to fetch file: ${link.href}`);
- const blob = await fetchFileWithRetry(link.href);
- const filename = link.textContent.trim() || `file_${Math.random().toString(36).substring(7)}`;
- console.log(`Adding file to ZIP: ${filename}`);
- zip.file(filename, blob);
- } catch (error) {
- console.error(error.message);
- }
- });
- // Run tasks with controlled concurrency
- await limitConcurrency(MAX_CONCURRENT_DOWNLOADS, tasks);
- // console.log('All files fetched, generating ZIP');
- // Generate ZIP and trigger download
- zip.generateAsync({ type: "blob" }).then(content => {
- saveAs(content, `${folderTitle}.zip`);
- loaderSpanAll.style.display = 'none';
- console.log('ZIP generated and download triggered');
- }).catch(error => {
- console.error('Error generating ZIP:', error);
- loaderSpanAll.style.display = 'none';
- alert('Error occurred while generating ZIP.');
- });
- });
- // Add individual download buttons next to each file link
- const fileLinks = document.querySelectorAll('tr td a[href*="/download/"]');
- fileLinks.forEach(link => {
- const dlButton = document.createElement('button');
- dlButton.innerHTML = 'DL';
- const loaderSpan = document.createElement('span');
- loaderSpan.className = 'loader';
- loaderSpan.style.display = 'none';
- dlButton.appendChild(loaderSpan);
- dlButton.className = 'dl-button';
- dlButton.addEventListener('click', async (e) => {
- e.preventDefault();
- console.log(`Individual download button clicked for link: ${link.href}`);
- loaderSpan.style.display = 'inline-block';
- // console.log('Loader displayed for individual download button');
- try {
- // First visit the link to prepare the direct download
- await fetch(link.href, { method: 'GET', mode: 'no-cors' });
- const directUrl = link.href.replace('/download/', '/cdn/download/') + '/?dl';
- console.log(`Fetching direct URL: ${directUrl}`);
- const response = await fetch(directUrl);
- if (!response.ok) {
- throw new Error(`Failed to download: ${response.statusText}`);
- }
- const blob = await response.blob();
- const filename = link.textContent.trim();
- console.log(`Successfully downloaded file: ${filename}`);
- const a = document.createElement('a');
- a.href = URL.createObjectURL(blob);
- a.download = filename;
- document.body.appendChild(a);
- a.click();
- document.body.removeChild(a);
- console.log(`File download triggered: ${filename}`);
- } catch (error) {
- console.error('Error downloading file:', error);
- alert(`Error downloading file: ${link.textContent.trim()}`);
- } finally {
- loaderSpan.style.display = 'none';
- // console.log('Loader hidden for individual download button');
- }
- });
- link.parentNode.appendChild(dlButton);
- // console.log('DL button appended for link:', link.textContent);
- });
- });
- })();