您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Greasy Fork is available in English.
支援下载 Arca Live 贴文中的图片、GIF、MP4、WEBM(使用 GM_download 绕过 CORS)并自动命名为「板块_编号_0001~n」格式。
// ==UserScript== // @name Arca Live Image and Video Downloader // @name:zh-TW Arca Live 圖片與影片下載器 // @name:zh-CN Arca Live 图片和视频下载器 // @namespace http://tampermonkey.net/ // @version 1.7 // @description Supports downloading images, GIFs, MP4s, and WEBMs from Arca Live posts (using GM_download to bypass CORS), and automatically names the files in the format: Board_PostNumber_0001~n // @description:zh-TW 支援下載 Arca Live 貼文中的圖片、GIF、MP4、WEBM(使用 GM_download 繞過 CORS)並自動命名為「板塊_編號_0001~n」格式。 // @description:zh-CN 支援下载 Arca Live 贴文中的图片、GIF、MP4、WEBM(使用 GM_download 绕过 CORS)并自动命名为「板块_编号_0001~n」格式。 // @author ChatGPT // @match https://arca.live/* // @grant GM_download // @license MIT // ==/UserScript== (function () { 'use strict'; // 延遲用的 sleep 函數(毫秒) const sleep = ms => new Promise(resolve => setTimeout(resolve, ms)); // 等待 Arca Live 收藏按鈕出現,作為我們插入自訂按鈕的參考點 const waitForScrapButton = async () => { for (let i = 0; i < 50; i++) { const scrapBtn = document.querySelector('form#scrapForm > button.scrap-btn'); if (scrapBtn) return scrapBtn; await sleep(200); // 每次等待 200 毫秒 } return null; }; // 收集所有圖片與影片的網址(支援 JPG、PNG、GIF、MP4、WEBM) const collectMediaUrls = () => { const urls = new Set(); // 收集 <img> 標籤(含 data-src) document.querySelectorAll('.article-body img').forEach(img => { const src = img.getAttribute('data-src') || img.src; if (src) urls.add(src); }); // 收集 <video><source src="..."> 影片來源 document.querySelectorAll('.article-body video source').forEach(source => { if (source.src) urls.add(source.src); }); // 收集 <video src="..."> 的影片來源 document.querySelectorAll('.article-body video').forEach(video => { if (video.src) urls.add(video.src); }); // 收集 <a href="xxx.gif/mp4/webm"> 的連結(有些影片或動畫是這種形式) document.querySelectorAll('.article-body a[href]').forEach(a => { const href = a.href; if (/\.(gif|mp4|webm)(\?.*)?$/i.test(href)) { urls.add(href); } }); return Array.from(urls); // 將 Set 轉為陣列並回傳 }; // 從網址中解析出板塊名稱與貼文 ID const parseBoardInfo = () => { const match = location.pathname.match(/^\/b\/([^/]+)\/(\d+)/); if (!match) return { board: 'unknown', postId: 'unknown' }; return { board: match[1], postId: match[2] }; }; // 使用 GM_download 逐一下載媒體檔案 const downloadMedia = async (urls, button) => { const { board, postId } = parseBoardInfo(); let success = 0; for (let i = 0; i < urls.length; i++) { const url = urls[i]; const ext = url.split('.').pop().split('?')[0].split('#')[0] || 'bin'; // 取得副檔名 const filename = `${board}_${postId}_${String(i + 1).padStart(4, '0')}.${ext}`; // 組合檔名 try { GM_download({ url, name: filename, saveAs: false, onload: () => { success++; button.textContent = `下載中 (${success}/${urls.length})`; }, onerror: (err) => { console.warn(`❌ 無法下載: ${url}`, err); } }); } catch (e) { console.error(`❌ GM_download 錯誤: ${url}`, e); } await sleep(500); // 每張圖片間隔 500ms,避免過快觸發限制 } // 完成後提示 button.textContent = '✅ 下載完成'; setTimeout(() => { button.disabled = false; button.textContent = '📥 逐張圖片下載'; }, 4000); }; // 建立「📥 逐張圖片下載」按鈕 const createDownloadButton = () => { const btn = document.createElement('button'); btn.textContent = '📥 逐張圖片下載'; btn.className = 'btn btn-arca btn-sm float-left mr-2'; // 插入到收藏按鈕左側,float-left 並加右邊距 btn.type = 'button'; btn.addEventListener('click', async () => { btn.disabled = true; btn.textContent = '🔄 收集媒體中...'; const urls = collectMediaUrls(); if (urls.length === 0) { alert('⚠️ 找不到任何圖片或影片'); btn.disabled = false; btn.textContent = '📥 逐張圖片下載'; return; } await downloadMedia(urls, btn); }); return btn; }; // 插入按鈕到收藏按鈕的左側 const insertButton = async () => { const scrapBtn = await waitForScrapButton(); if (!scrapBtn) return; const downloadBtn = createDownloadButton(); scrapBtn.parentElement.insertBefore(downloadBtn, scrapBtn); // 插入左側 }; insertButton(); })();