// ==UserScript== // @name 豆包视频图集解析下载 // @namespace http://309096184.xyz/ // @version 1.0.0 // @description 在豆包分享页面(视频/图集)一键解析作品,下载无水印作品 // @author 【Rain】黯笙 // @icon http://309096184.xyz/烧包/三楼插件/jbtx.png // @match *://*.doubao.com/* // @grant GM_xmlhttpRequest // @grant GM_download // @run-at document-start // @license 黯笙 // ==/UserScript== (function() { const textBtn = "解析"; const textFail = "解析失败"; const textError = "解析出错"; const textClose = "关闭"; const loadingTipText = "正在解析当前作品..."; const textDonate = "赞助"; const donateTip = "感谢支持作者【Rain】黯笙"; const donateImgUrl = "http://309096184.xyz/x/zz.png"; const updateCheckUrl = "http://309096184.xyz/烧包/脚本猫插件/gx.txt"; const currentVersion = "1.0.0"; const textUpdate = "当前版本过低\n请前往更新\n作者【Rain】黯笙"; const bgColor = "#1c1c1e"; const cardColor = "#252527"; const textColor = "#e5e5e7"; const subTextColor = "#8e8e93"; const btnBgColor = cardColor; const innerGlow = "inset 0 0 6px rgba(255,255,255,0.1)"; const innerGlowPressed = "inset 0 0 3px rgba(255,255,255,0.06)"; function versionIsHigher(v1, v2) { const a = v1.split(".").map(Number); const b = v2.split(".").map(Number); for (let i = 0; i < Math.max(a.length, b.length); i++) { if ((a[i] || 0) > (b[i] || 0)) return true; if ((a[i] || 0) < (b[i] || 0)) return false; } return false; } function showLoadingToast(msg) { const old = document.getElementById("parseLoadingToast"); if (old) old.remove(); const toast = document.createElement("div"); toast.id = "parseLoadingToast"; toast.textContent = msg; Object.assign(toast.style, { position: "fixed", top: "50%", left: "50%", transform: "translate(-50%,-50%)", background: cardColor, color: textColor, padding: "14px 24px", borderRadius: "20px", fontSize: "15px", zIndex: "2147483647", fontFamily: "-apple-system, BlinkMacSystemFont, 'SF Pro Text', sans-serif", boxShadow: innerGlow }); document.body.appendChild(toast); return toast; } let analyzeBtn = null; function createAnalyzeBtn() { if (analyzeBtn) return analyzeBtn; const btn = document.createElement("button"); btn.textContent = textBtn; btn.id = "doubaoParseFloatBtn"; Object.assign(btn.style, { position: "fixed", bottom: "120px", right: "20px", zIndex: "2147483647", width: "56px", height: "56px", borderRadius: "50%", background: btnBgColor, color: "#ffffff", border: "none", fontSize: "14px", fontWeight: "600", fontFamily: "-apple-system, BlinkMacSystemFont, 'SF Pro Text', sans-serif", boxShadow: innerGlow, transition: "box-shadow 0.15s, transform 0.15s" }); btn.addEventListener("pointerdown", () => { btn.style.boxShadow = innerGlowPressed; btn.style.transform = "scale(0.92)"; }); btn.addEventListener("pointerup", () => { btn.style.boxShadow = innerGlow; btn.style.transform = "scale(1)"; }); return btn; } function showBtn() { if (!analyzeBtn) analyzeBtn = createAnalyzeBtn(); if (!analyzeBtn.parentElement) document.body.appendChild(analyzeBtn); analyzeBtn.style.display = "block"; } function hideBtn() { if (analyzeBtn) analyzeBtn.style.display = "none"; } function toggleBtn() { if (location.hostname.includes("doubao.com")) showBtn(); else hideBtn(); } function startAnalyze() { const loadingToast = showLoadingToast(loadingTipText); const apiUrl = `http://309096184.xyz/烧包/脚本猫插件/包子.php?token=K8x9Lm2Qp4R7vW1zY5aB&url=${encodeURIComponent(location.href)}`; GM_xmlhttpRequest({ method: "GET", url: apiUrl, timeout: 30000, onload: function(res) { loadingToast.remove(); try { const data = JSON.parse(res.responseText); if (data.code !== 200 || !data.data || !data.data.type) { alert(`${textFail}:未检测到可解析的作品`); return; } renderDoubaoPopup(data.data); } catch (e) { alert(textError); console.error(e); } }, onerror: () => { loadingToast.remove(); alert(textError + ",接口请求失败"); }, ontimeout: () => { loadingToast.remove(); alert("请求超时"); } }); } function openDonatePopup() { const mask = document.createElement("div"); Object.assign(mask.style, { position: "fixed", inset: "0", background: "rgba(0,0,0,0.7)", zIndex: "2147483648", display: "flex", alignItems: "center", justifyContent: "center" }); const card = document.createElement("div"); Object.assign(card.style, { background: cardColor, borderRadius: "24px", padding: "24px", maxWidth: "320px", width: "90%", textAlign: "center", boxShadow: innerGlow }); const title = document.createElement("div"); title.textContent = donateTip; Object.assign(title.style, { color: textColor, fontSize: "17px", fontWeight: "600", marginBottom: "20px", fontFamily: "-apple-system, sans-serif" }); const qrWrap = document.createElement("div"); Object.assign(qrWrap.style, { width: "100%", minHeight: "220px", background: bgColor, borderRadius: "18px", display: "flex", alignItems: "center", justifyContent: "center", color: subTextColor, fontSize: "14px", marginBottom: "20px", boxShadow: innerGlow }); qrWrap.textContent = "加载中..."; const closeBtn = document.createElement("button"); closeBtn.textContent = textClose; Object.assign(closeBtn.style, { background: btnBgColor, color: "#ffffff", border: "none", borderRadius: "14px", padding: "10px 32px", fontSize: "16px", fontWeight: "500", fontFamily: "-apple-system, sans-serif", boxShadow: innerGlow }); closeBtn.addEventListener("pointerdown", () => closeBtn.style.boxShadow = innerGlowPressed); closeBtn.addEventListener("pointerup", () => closeBtn.style.boxShadow = innerGlow); closeBtn.onclick = () => mask.remove(); card.appendChild(title); card.appendChild(qrWrap); card.appendChild(closeBtn); mask.appendChild(card); document.body.appendChild(mask); mask.onclick = (e) => { if (e.target === mask) mask.remove(); }; GM_xmlhttpRequest({ method: "GET", url: donateImgUrl, responseType: "blob", timeout: 10000, onload: function(res) { if (res.status >= 200 && res.status < 300) { const blobUrl = URL.createObjectURL(res.response); const img = document.createElement("img"); img.src = blobUrl; img.style.cssText = "width:100%;height:auto;object-fit:contain;border-radius:12px;"; qrWrap.innerHTML = ""; qrWrap.appendChild(img); } else qrWrap.innerHTML = '加载失败'; }, onerror: () => qrWrap.innerHTML = '网络错误', ontimeout: () => qrWrap.innerHTML = '超时' }); } function downloadFile(url, filename) { GM_download({ url, name: filename, saveAs: false }); } function copyText(text) { if (navigator.clipboard && window.isSecureContext) { navigator.clipboard.writeText(text).then(() => alert("已复制到剪贴板")).catch(() => alert("复制失败")); } else { const ta = document.createElement("textarea"); ta.value = text; ta.style.position = "fixed"; ta.style.opacity = "0"; document.body.appendChild(ta); ta.select(); try { document.execCommand("copy"); alert("已复制"); } catch (e) { alert("复制失败"); } document.body.removeChild(ta); } } function createButton(text, callback) { const btn = document.createElement("button"); btn.textContent = text; Object.assign(btn.style, { display: "block", width: "100%", background: btnBgColor, color: "#ffffff", border: "none", borderRadius: "14px", padding: "12px", fontSize: "16px", fontWeight: "500", fontFamily: "-apple-system, BlinkMacSystemFont, 'SF Pro Text', sans-serif", marginBottom: "12px", boxShadow: innerGlow, transition: "box-shadow 0.15s, transform 0.15s" }); btn.addEventListener("pointerdown", () => { btn.style.boxShadow = innerGlowPressed; btn.style.transform = "scale(0.98)"; }); btn.addEventListener("pointerup", () => { btn.style.boxShadow = innerGlow; btn.style.transform = "scale(1)"; }); btn.onclick = callback; return btn; } function renderDoubaoPopup(data) { const mask = document.createElement("div"); Object.assign(mask.style, { position: "fixed", inset: "0", background: bgColor, zIndex: "2147483646", overflowY: "auto", paddingTop: "90px", paddingLeft: "16px", paddingRight: "16px", paddingBottom: "40px", fontFamily: "-apple-system, BlinkMacSystemFont, 'SF Pro Text', 'Helvetica Neue', sans-serif" }); const topBar = document.createElement("div"); Object.assign(topBar.style, { position: "fixed", top: "0", left: "0", right: "0", background: cardColor, zIndex: "999", padding: "12px 16px", display: "flex", justifyContent: "space-between", alignItems: "center", boxShadow: innerGlow, borderBottom: "1px solid rgba(255,255,255,0.05)" }); const titleSpan = document.createElement("span"); titleSpan.style.cssText = `color:${textColor};font-size:18px;font-weight:600;`; titleSpan.textContent = data.type === "video" ? "\u25B6 视频" : "\u25A3 图集"; const btnGroup = document.createElement("div"); btnGroup.style.cssText = "display:flex;gap:8px;"; const donateBtn = document.createElement("button"); donateBtn.textContent = textDonate; Object.assign(donateBtn.style, { background: btnBgColor, color: "#ffffff", border: "none", borderRadius: "14px", padding: "8px 14px", fontSize: "14px", fontWeight: "500", fontFamily: "inherit", boxShadow: innerGlow }); donateBtn.addEventListener("pointerdown", () => donateBtn.style.boxShadow = innerGlowPressed); donateBtn.addEventListener("pointerup", () => donateBtn.style.boxShadow = innerGlow); donateBtn.onclick = openDonatePopup; const closeBtn = document.createElement("button"); closeBtn.textContent = textClose; Object.assign(closeBtn.style, { background: btnBgColor, color: "#ffffff", border: "none", borderRadius: "14px", padding: "8px 18px", fontSize: "14px", fontWeight: "500", fontFamily: "inherit", boxShadow: innerGlow }); closeBtn.addEventListener("pointerdown", () => closeBtn.style.boxShadow = innerGlowPressed); closeBtn.addEventListener("pointerup", () => closeBtn.style.boxShadow = innerGlow); closeBtn.onclick = () => mask.remove(); btnGroup.appendChild(donateBtn); btnGroup.appendChild(closeBtn); topBar.appendChild(titleSpan); topBar.appendChild(btnGroup); mask.appendChild(topBar); const content = document.createElement("div"); content.style.cssText = "max-width:600px;margin:0 auto;"; if (data.desc || data.title) { const descCard = document.createElement("div"); Object.assign(descCard.style, { background: cardColor, borderRadius: "20px", padding: "16px", marginBottom: "20px", color: textColor, fontSize: "15px", lineHeight: "1.6", boxShadow: innerGlow, display: "flex", justifyContent: "space-between", alignItems: "flex-start", gap: "12px" }); const descText = document.createElement("div"); descText.textContent = data.desc || data.title; descText.style.cssText = "white-space:pre-wrap;word-break:break-word;flex:1;"; const copyBtn = document.createElement("button"); copyBtn.textContent = "复制描述"; Object.assign(copyBtn.style, { background: btnBgColor, color: "#ffffff", border: "none", borderRadius: "8px", padding: "4px 12px", fontSize: "12px", fontWeight: "500", flexShrink: "0", marginTop: "2px", boxShadow: innerGlow }); copyBtn.addEventListener("pointerdown", () => copyBtn.style.boxShadow = innerGlowPressed); copyBtn.addEventListener("pointerup", () => copyBtn.style.boxShadow = innerGlow); copyBtn.onclick = () => copyText(data.desc || data.title); descCard.appendChild(descText); descCard.appendChild(copyBtn); content.appendChild(descCard); } if (data.author && data.author.name) { const authorCard = document.createElement("div"); Object.assign(authorCard.style, { display: "flex", alignItems: "center", gap: "14px", background: cardColor, borderRadius: "20px", padding: "14px", marginBottom: "20px", boxShadow: innerGlow }); if (data.author.avatar) { const avt = document.createElement("img"); avt.src = data.author.avatar; Object.assign(avt.style, { width: "48px", height: "48px", borderRadius: "50%", objectFit: "cover", border: "2px solid rgba(255,255,255,0.15)", boxShadow: "none" }); authorCard.appendChild(avt); } const nameWrap = document.createElement("div"); const nameDiv = document.createElement("div"); nameDiv.style.cssText = `color:${textColor};font-size:16px;font-weight:500;`; nameDiv.textContent = data.author.name; nameWrap.appendChild(nameDiv); if (data.author.id) { const idDiv = document.createElement("div"); idDiv.style.cssText = `color:${subTextColor};font-size:13px;`; idDiv.textContent = "ID: " + data.author.id; nameWrap.appendChild(idDiv); } authorCard.appendChild(nameWrap); content.appendChild(authorCard); } if (data.type === "video") { if (data.cover) { const cover = document.createElement("img"); cover.src = data.cover; cover.style.cssText = `width:100%;border-radius:16px;display:block;margin-bottom:14px;box-shadow:${innerGlow};`; content.appendChild(cover); } if (data.url) { const loading = document.createElement("div"); loading.textContent = "视频加载中..."; loading.style.cssText = `color:${subTextColor};text-align:center;padding:28px;font-size:15px;`; content.appendChild(loading); const video = document.createElement("video"); video.controls = true; video.style.cssText = `width:100%;border-radius:16px;margin-bottom:12px;display:none;box-shadow:${innerGlow};`; content.appendChild(video); GM_xmlhttpRequest({ method: "GET", url: data.url, responseType: "blob", timeout: 120000, onload: function(res) { loading.remove(); if (res.status >= 200 && res.status < 300) { const blobUrl = URL.createObjectURL(res.response); video.src = blobUrl; video.style.display = "block"; } else { const err = document.createElement("div"); err.textContent = "视频加载失败"; err.style.cssText = `color:#ff453a;text-align:center;margin-bottom:12px;`; content.insertBefore(err, video); } }, onerror: () => { loading.remove(); video.remove(); content.appendChild(Object.assign(document.createElement("div"), { textContent: "网络错误", style: `color:#ff453a;text-align:center;margin-bottom:12px;` })); }, ontimeout: () => { loading.remove(); video.remove(); content.appendChild(Object.assign(document.createElement("div"), { textContent: "加载超时", style: `color:#ff453a;text-align:center;margin-bottom:12px;` })); } }); content.appendChild(createButton("下载视频", () => { downloadFile(data.url, (data.title || data.desc || "video") + ".mp4"); })); } } else if (data.type === "image" && data.images && data.images.length > 0) { if (data.images.length > 1) { content.appendChild(createButton("一键下载全部 (" + data.images.length + "张)", () => { data.images.forEach((url, idx) => { setTimeout(() => downloadFile(url, (data.title || data.desc || "image") + `_${idx+1}.jpg`), idx * 300); }); alert("开始批量下载,请允许浏览器多次保存"); })); } data.images.forEach((imgUrl, idx) => { const img = document.createElement("img"); img.src = imgUrl; img.style.cssText = `width:100%;border-radius:16px;display:block;margin-bottom:10px;box-shadow:${innerGlow};`; content.appendChild(img); content.appendChild(createButton("下载图片 " + (idx+1), () => { downloadFile(imgUrl, (data.title || data.desc || "image") + `_${idx+1}.jpg`); })); }); } mask.appendChild(content); document.body.appendChild(mask); } function init() { if (!document.body) { setTimeout(init, 50); return; } toggleBtn(); analyzeBtn.onclick = () => { GM_xmlhttpRequest({ method: "GET", url: updateCheckUrl, timeout: 5000, onload: function(res) { try { const info = JSON.parse(res.responseText); if (versionIsHigher(info.version, currentVersion) && info.forceUpdate) { alert(textUpdate); if (info.updateUrl) window.open(info.updateUrl); return; } } catch (e) {} startAnalyze(); }, onerror: startAnalyze, ontimeout: startAnalyze }); }; setInterval(toggleBtn, 800); } init(); })();