// ==UserScript== // @name 图片/视频上传助手(智能命名版) // @namespace https://example.com/ // @version 1.2 // @description 根据标签内容智能命名文件,支持图片/视频上传到指定 API,含悬浮、菜单、剪贴板上传功能 // @author 云空陆 // @license MIT // @match *://*/* // @grant GM_xmlhttpRequest // @grant GM_registerMenuCommand // @connect your.domain // @downloadURL none // ==/UserScript== (function () { 'use strict'; /*********** 配置区域 ***********/ const UPLOAD_API = ""; // 上传 API直接填写api地址 const AUTH_CODE = ""; // 上传认证码或者apitoken(可为空) const SERVER_COMPRESS = "false"; // true/false/空(空使用默认 true) const UPLOAD_CHANNEL = ""; // telegram/cfr2/s3/空(空使用默认 telegram) const AUTO_RETRY = ""; // true/false/空(空使用默认 true) const UPLOAD_NAME_TYPE = ""; // default/index/origin/short/空(空使用默认 default) const RETURN_FORMAT = ""; // default/full/空(空使用默认 default) const UPLOAD_FOLDER = ""; // 相对目录,空使用默认 /********************************/ let currentFile = null; const uploadBtn = document.createElement("div"); uploadBtn.innerText = "📤"; Object.assign(uploadBtn.style, { position: "fixed", padding: "6px 10px", background: "#f13535ff", color: "#fff", borderRadius: "4px", fontSize: "12px", cursor: "pointer", zIndex: "999999", display: "none", boxShadow: "0 2px 6px rgba(0,0,0,0.3)", transition: "opacity 0.2s", }); document.body.appendChild(uploadBtn); let hideUploadTimer = null; function getFileSrc(el) { return el.getAttribute('data-original') || el.getAttribute('data-src') || el.src || ''; } // 获取节点中可读标题作为文件名 function getReadableName(el) { const nameAttr = el.getAttribute("alt") || el.getAttribute("title") || el.getAttribute("aria-label") || el.getAttribute("data-name") || el.getAttribute("data-title") || ""; if (nameAttr && nameAttr.trim()) return sanitizeFilename(nameAttr.trim()); return ""; } // 清理非法文件名字符 function sanitizeFilename(name) { return name.replace(/[\\/:*?"<>|]/g, "_").slice(0, 50); } // 根据 MIME 类型或 URL 自动获取扩展名 function guessExtension(mime, url = "") { if (mime) { if (mime.startsWith("image/")) return mime.split("/")[1]; if (mime.startsWith("video/")) return mime.split("/")[1]; } const m = url.match(/\.(jpg|jpeg|png|webp|gif|mp4|mov|avi|mkv|webm|svg|bmp|heic|heif)(\?|#|$)/i); if (m) return m[1].toLowerCase(); return "dat"; } // ===== 鼠标悬浮显示按钮 ===== document.addEventListener("mouseover", (e) => { let el = e.target; if ((el.tagName === "IMG" || el.tagName === "VIDEO") && getFileSrc(el)) { currentFile = el; const rect = el.getBoundingClientRect(); uploadBtn.style.left = `${rect.right - 160}px`; uploadBtn.style.top = `${rect.top + 10}px`; uploadBtn.style.display = "block"; uploadBtn.style.opacity = "1"; clearTimeout(hideUploadTimer); } }); document.addEventListener("mouseout", (e) => { if (!uploadBtn.contains(e.relatedTarget) && (!currentFile || !currentFile.contains(e.relatedTarget))) { clearTimeout(hideUploadTimer); hideUploadTimer = setTimeout(() => { uploadBtn.style.opacity = "0"; setTimeout(() => { uploadBtn.style.display = "none"; }, 300); }, 500); } }); uploadBtn.addEventListener("mouseenter", () => clearTimeout(hideUploadTimer)); // ===== 上传核心逻辑 ===== async function uploadFileByUrl(fileUrl, index = 1, total = 1, el = null) { if (!fileUrl) { showTip("❌ 未找到文件链接", true); return; } showTip(`📤 上传中(${index}/${total})\n${fileUrl}`, false); try { const resp = await fetch(fileUrl); const blob = await resp.blob(); // 智能生成文件名 let baseName = el ? getReadableName(el) : ""; const ext = guessExtension(blob.type, fileUrl); if (!baseName) { const fromUrl = decodeURIComponent(fileUrl.split("/").pop().split("?")[0]); baseName = sanitizeFilename(fromUrl.replace(/\.[a-z0-9]+$/i, "")) || `file_${Date.now()}`; } const filename = `${baseName}.${ext}`; const formData = new FormData(); formData.append("file", blob, filename); const queryParams = new URLSearchParams(); if (AUTH_CODE) queryParams.append("authCode", AUTH_CODE); if (SERVER_COMPRESS) queryParams.append("serverCompress", SERVER_COMPRESS); if (UPLOAD_CHANNEL) queryParams.append("uploadChannel", UPLOAD_CHANNEL); if (AUTO_RETRY) queryParams.append("autoRetry", AUTO_RETRY); if (UPLOAD_NAME_TYPE) queryParams.append("uploadNameType", UPLOAD_NAME_TYPE); if (RETURN_FORMAT) queryParams.append("returnFormat", RETURN_FORMAT); if (UPLOAD_FOLDER) queryParams.append("uploadFolder", UPLOAD_FOLDER); GM_xmlhttpRequest({ method: "POST", url: `${UPLOAD_API}?${queryParams.toString()}`, data: formData, responseType: "json", onload: function (res) { let result; try { result = res.response || JSON.parse(res.responseText); } catch (e) { showTip("❌ 上传响应解析失败", true); return; } if (Array.isArray(result) && result[0]?.src) { const fullUrl = `${UPLOAD_API.replace(/\/upload$/, "")}${result[0].src}`; showTip(`✅ 上传成功(${index}/${total})\n${filename}\n链接: ${fullUrl}`, true, fullUrl); console.log("上传成功:", fullUrl); } else { showTip(`❌ 上传失败(${index}/${total}): 未返回有效链接`, true); console.error(result); } }, onerror: function () { showTip("❌ 网络请求失败", true); } }); } catch (err) { console.error(err); showTip("❌ 文件加载或处理失败", true); } } uploadBtn.addEventListener("click", async () => { if (!currentFile) return; const url = getFileSrc(currentFile); await uploadFileByUrl(url, 1, 1, currentFile); }); // ===== 提示框 ===== let tipDiv = null, autoCloseTimer = null; function showTip(msg, showControls = false, copyText = "") { if (!tipDiv) { tipDiv = document.createElement("div"); Object.assign(tipDiv.style, { position: "fixed", top: "20px", right: "20px", background: "rgba(0,0,0,0.55)", color: "#fff", padding: "10px 15px", borderRadius: "6px", zIndex: "999999", fontSize: "13px", whiteSpace: "pre-wrap", wordBreak: "break-word", maxWidth: "320px", maxHeight: "250px", overflowY: "auto" }); document.body.appendChild(tipDiv); } tipDiv.innerHTML = ""; const textNode = document.createElement("div"); textNode.innerText = msg; tipDiv.appendChild(textNode); if (showControls) { const btnContainer = document.createElement("div"); Object.assign(btnContainer.style, { marginTop: "6px", textAlign: "right" }); if (copyText) { const copyBtn = document.createElement("button"); copyBtn.innerText = "复制链接"; Object.assign(copyBtn.style, { marginRight: "5px", cursor: "pointer", color: "#fff", background: "#f13535ff", border: "none", borderRadius: "4px", padding: "3px 8px", fontSize: "12px" }); copyBtn.onclick = () => { navigator.clipboard.writeText(copyText).then(() => { copyBtn.innerText = "已复制"; setTimeout(() => copyBtn.innerText = "复制链接", 1500); }); }; btnContainer.appendChild(copyBtn); } const closeBtn = document.createElement("button"); closeBtn.innerText = "关闭"; Object.assign(closeBtn.style, { cursor: "pointer", color: "#fff", background: "#f13535ff", border: "none", borderRadius: "4px", padding: "3px 8px", fontSize: "12px" }); closeBtn.onclick = () => { tipDiv.style.display = "none"; clearTimeout(autoCloseTimer); }; btnContainer.appendChild(closeBtn); tipDiv.appendChild(btnContainer); } tipDiv.style.display = "block"; clearTimeout(autoCloseTimer); autoCloseTimer = setTimeout(() => { tipDiv.style.display = "none"; }, 10000); } // ===== 菜单上传 ===== GM_registerMenuCommand("📥 手动输入链接上传", async () => { const input = prompt("请输入文件链接(可输入多个链接,用换行或逗号分隔):"); if (!input) return; const urls = input.split(/\n|,|;|\s+/).map(u => u.trim()).filter(Boolean); const total = urls.length; for (let i = 0; i < total; i++) { await uploadFileByUrl(urls[i], i + 1, total); await new Promise(r => setTimeout(r, 1200)); } showTip(`✅ 全部上传完成,共 ${total} 个文件`, true); }); // ===== 剪贴板上传按钮 ===== const clipBtn = document.createElement("div"); clipBtn.innerText = "📋剪贴图床"; Object.assign(clipBtn.style, { position: "fixed", right: localStorage.getItem("clipBtn_right") || "20px", bottom: localStorage.getItem("clipBtn_bottom") || "80px", background: "#f13535ff", color: "#fff", padding: "8px 12px", borderRadius: "20px", fontSize: "13px", cursor: "pointer", boxShadow: "0 2px 6px rgba(0,0,0,0.3)", zIndex: "999999", opacity: "0.9", userSelect: "none", }); document.body.appendChild(clipBtn); // ===== 拖拽逻辑 ===== let isDragging = false, offsetX = 0, offsetY = 0; clipBtn.addEventListener("mousedown", (e) => { isDragging = true; offsetX = e.clientX - clipBtn.getBoundingClientRect().left; offsetY = e.clientY - clipBtn.getBoundingClientRect().top; clipBtn.style.transition = "none"; e.preventDefault(); }); document.addEventListener("mousemove", (e) => { if (!isDragging) return; let left = e.clientX - offsetX; let top = e.clientY - offsetY; left = Math.max(0, Math.min(window.innerWidth - clipBtn.offsetWidth, left)); top = Math.max(0, Math.min(window.innerHeight - clipBtn.offsetHeight, top)); clipBtn.style.left = left + "px"; clipBtn.style.top = top + "px"; clipBtn.style.right = "auto"; clipBtn.style.bottom = "auto"; }); document.addEventListener("mouseup", () => { if (isDragging) { isDragging = false; clipBtn.style.transition = "opacity 0.2s"; localStorage.setItem("clipBtn_left", clipBtn.style.left); localStorage.setItem("clipBtn_top", clipBtn.style.top); } }); // ===== 剪贴板上传逻辑 ===== clipBtn.addEventListener("click", async () => { if (isDragging) return; try { const items = await navigator.clipboard.read(); for (const item of items) { for (const type of item.types) { if (type.startsWith("image/") || type.startsWith("video/")) { const blob = await item.getType(type); const blobUrl = URL.createObjectURL(blob); showTip("📋 检测到文件,正在上传...", false); await uploadFileByUrl(blobUrl); return; } } } const text = await navigator.clipboard.readText(); if (/^https?:\/\/[^\s]+/i.test(text.trim())) { showTip("📋 检测到文件链接,正在上传...", false); await uploadFileByUrl(text.trim()); } else { showTip("⚠️ 剪贴板中未检测到文件或链接", true); } } catch (err) { console.error(err); showTip("❌ 无法访问剪贴板,请检查浏览器权限", true); } }); })();