// ==UserScript== // @name X(Twitter)推特解析下载 // @namespace http://309096184.xyz/ // @version 1.0.0 // @description 自动识别X/Twitter推文链接,一键解析无水印视频下载 // @author 【Rain】黯笙 // @icon http://309096184.xyz/x/tb.webp // @match *://*/* // @grant GM_xmlhttpRequest // @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 API_TOKEN = "7kRf2Dp9vBnXtG5cLz0JmY8aP3sE6wU1hQ4dF"; const BACKEND_URL = "http://309096184.xyz/x/x.php"; const updateCheckUrl = "http://309096184.xyz/xcj/gx.txt"; const currentVersion = "1.0.0"; const textUpdate = "当前版本过低\n请前往葫芦侠三楼更新\n作者【Rain】黯笙"; function versionIsHigher(v1, v2) { const arr1 = v1.split(".").map(Number); const arr2 = v2.split(".").map(Number); const maxLen = Math.max(arr1.length, arr2.length); for(let i = 0; i < maxLen; i++){ const num1 = arr1[i] || 0; const num2 = arr2[i] || 0; if(num1 > num2) return true; if(num1 < num2) return false; } return false; } function cleanTweetUrl(url) { let u = url.trim(); u = u.replace(/#.*/, ""); u = u.replace(/(\/status\/\d+).*/, "$1"); return u; } function isTweetUrl(url) { return /(?:twitter\.com|x\.com)\/[^\/]+\/status\/\d+/i.test(url); } function showLoadingToast(msg) { const oldToast = document.getElementById("parseLoadingToast"); if (oldToast) oldToast.remove(); const toast = document.createElement("div"); toast.id = "parseLoadingToast"; toast.textContent = msg; toast.style.cssText = [ "position:fixed !important", "top:50% !important", "left:50% !important", "transform:translate(-50%,-50%) !important", "background:rgba(0,0,0,0.75) !important", "color:#fff !important", "padding:14px 26px !important", "border-radius:8px !important", "font-size:15px !important", "z-index:2147483647 !important", "white-space:nowrap !important", "font-family:Arial,Helvetica,sans-serif !important", "writing-mode:horizontal-tb !important", "direction:ltr !important", "text-align:center !important" ].join(";"); 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 = "xParseFloatBtn"; btn.style.cssText = [ "position:fixed !important", "bottom:120px !important", "right:20px !important", "z-index:2147483647 !important", "width:60px !important", "height:60px !important", "border-radius:50% !important", "background:#007aff !important", "color:#ffffff !important", "border:none !important", "font-size:14px !important", "font-weight:600 !important", "box-shadow:0 6px 16px rgba(0,122,255,0.4) !important", "pointer-events:auto !important", "margin:0 !important", "padding:0 !important", "line-height:60px !important", "text-align:center !important", "font-family:Arial,Helvetica,sans-serif !important", "writing-mode:horizontal-tb !important", "direction:ltr !important", "text-shadow:none !important", "text-transform:none !important", "letter-spacing:normal !important", "word-spacing:normal !important", "clip:auto !important", "clip-path:none !important", "transform:none !important" ].join(";"); 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 (isTweetUrl(location.href)) { showBtn(); } else { hideBtn(); } } function startAnalyze() { const loadingToast = showLoadingToast(loadingTipText); const cleanUrl = cleanTweetUrl(location.href); const fullApi = `${BACKEND_URL}?token=${API_TOKEN}&url=${encodeURIComponent(cleanUrl)}`; GM_xmlhttpRequest({ method: "GET", url: fullApi, timeout: 30000, onload: function(res) { loadingToast.remove(); try { const data = JSON.parse(res.responseText); if (data.code !== 200) { alert(`${textFail}:${data.message}`); return; } const resData = data.data; renderPopup(resData); } catch (e) { alert(textError); console.log("解析异常:", e); } }, onerror: () => { loadingToast.remove(); alert(textError); }, ontimeout: () => { loadingToast.remove(); alert("请求超时,请稍后重试"); } }); } function openDonatePopup() { const donateMask = document.createElement("div"); donateMask.style.cssText = [ "position:fixed !important", "inset:0 !important", "background:rgba(0,0,0,0.85) !important", "z-index:2147483648 !important", "display:flex !important", "flex-direction:column !important", "align-items:center !important", "justify-content:center !important", "padding:20px !important", "box-sizing:border-box !important" ].join(";"); const wrapBox = document.createElement("div"); wrapBox.style.cssText = [ "background:#111 !important", "border-radius:16px !important", "padding:24px !important", "max-width:320px !important", "width:100% !important", "text-align:center !important" ].join(";"); const title = document.createElement("div"); title.style.cssText = [ "color:#fff !important", "font-size:18px !important", "margin-bottom:16px !important", "font-weight:bold !important" ].join(";"); title.textContent = donateTip; const qrWrap = document.createElement("div"); qrWrap.style.cssText = [ "width:100% !important", "min-height:240px !important", "background:#222 !important", "border-radius:12px !important", "margin-bottom:20px !important", "display:flex !important", "align-items:center !important", "justify-content:center !important", "color:#aaa !important" ].join(";"); qrWrap.textContent = "二维码加载中..."; const closeDonateBtn = document.createElement("button"); closeDonateBtn.textContent = textClose; closeDonateBtn.style.cssText = [ "background:#007aff !important", "color:#fff !important", "border:none !important", "border-radius:12px !important", "padding:8px 24px !important", "font-size:15px !important" ].join(";"); closeDonateBtn.onclick = () => donateMask.remove(); wrapBox.appendChild(title); wrapBox.appendChild(qrWrap); wrapBox.appendChild(closeDonateBtn); donateMask.appendChild(wrapBox); document.body.appendChild(donateMask); 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 qrImg = document.createElement("img"); qrImg.src = blobUrl; qrImg.style.cssText = "width:100%;height:auto;object-fit:contain;border-radius:12px;"; qrWrap.innerHTML = ""; qrWrap.appendChild(qrImg); }else{ qrWrap.innerHTML = '二维码加载失败,链接访问异常'; } }, onerror:function(){ qrWrap.innerHTML = '网络请求失败,请检查服务器'; }, ontimeout:function(){ qrWrap.innerHTML = '图片加载超时'; } }); donateMask.onclick = function(e) { if(e.target === donateMask) donateMask.remove(); } } function renderPopup(resData) { const mask = document.createElement("div"); mask.style.cssText = [ "position:fixed !important", "inset:0 !important", "background:#000000 !important", "z-index:2147483646 !important", "overflow-y:auto !important", "padding:24px 12px 40px !important", "box-sizing:border-box !important" ].join(";"); const topBar = document.createElement("div"); topBar.style.cssText = [ "display:flex !important", "justify-content:space-between !important", "align-items:center !important", "gap:10px !important", "margin-bottom:20px !important", "padding:0 4px !important" ].join(";"); const pageTitle = document.createElement("span"); pageTitle.style.cssText = [ "color:#fff !important", "font-size:18px !important", "font-weight:500 !important", "font-family:Arial,Helvetica,sans-serif !important" ].join(";"); const donateBtn = document.createElement("button"); donateBtn.textContent = textDonate; donateBtn.style.cssText = [ "background:#ff3b30 !important", "color:#fff !important", "border:0 !important", "border-radius:14px !important", "padding:9px 14px !important", "font-size:14px !important", "font-family:Arial,Helvetica,sans-serif !important" ].join(";"); donateBtn.onclick = openDonatePopup; const closeBtn = document.createElement("button"); closeBtn.textContent = textClose; closeBtn.style.cssText = [ "background:#007aff !important", "color:#fff !important", "border:0 !important", "border-radius:14px !important", "padding:9px 22px !important", "font-size:16px !important", "font-family:Arial,Helvetica,sans-serif !important" ].join(";"); closeBtn.onclick = () => mask.remove(); const btnWrap = document.createElement("div"); btnWrap.style.cssText = "display:flex; gap:8px;"; btnWrap.appendChild(donateBtn); btnWrap.appendChild(closeBtn); topBar.appendChild(pageTitle); topBar.appendChild(btnWrap); mask.appendChild(topBar); const authorBox = document.createElement("div"); authorBox.style.cssText = [ "display:flex !important", "align-items:center !important", "gap:10px !important", "background:#121212 !important", "border-radius:16px !important", "padding:10px !important", "margin-bottom:16px !important" ].join(";"); const avatarImg = document.createElement("img"); avatarImg.src = resData.author.avatar; avatarImg.style.cssText = [ "width:48px !important", "height:48px !important", "border-radius:50% !important", "object-fit:cover !important" ].join(";"); const nameWrap = document.createElement("div"); const nameText = document.createElement("div"); nameText.style.cssText = [ "color:#fff !important", "font-size:16px !important", "font-family:Arial,Helvetica,sans-serif !important" ].join(";"); nameText.textContent = resData.author.name; const userText = document.createElement("div"); userText.style.cssText = [ "color:#aaa !important", "font-size:13px !important", "font-family:Arial,Helvetica,sans-serif !important" ].join(";"); userText.textContent = "@" + resData.author.username; nameWrap.appendChild(nameText); nameWrap.appendChild(userText); authorBox.appendChild(avatarImg); authorBox.appendChild(nameWrap); mask.appendChild(authorBox); const tweetText = document.createElement("div"); tweetText.style.cssText = [ "color:#eee !important", "font-size:15px !important", "white-space:pre-wrap !important", "margin-bottom:20px !important", "line-height:1.6 !important", "padding:0 4px !important", "font-family:Arial,Helvetica,sans-serif !important" ].join(";"); tweetText.textContent = resData.tweet_info.text; mask.appendChild(tweetText); if (resData.video_list && resData.video_list.length > 0) { pageTitle.textContent = "🎬 X视频解析下载"; resData.video_list.forEach(videoItem => { const videoCard = document.createElement("div"); videoCard.style.cssText = [ "background:#121212 !important", "border-radius:16px !important", "overflow:hidden !important", "padding:8px !important", "margin-bottom:16px !important", "box-shadow:0 4px 14px rgba(0,0,0,0.5) !important" ].join(";"); const coverImg = document.createElement("img"); coverImg.src = videoItem.cover; coverImg.style.cssText = [ "width:100% !important", "border-radius:12px !important", "display:block !important" ].join(";"); const video = document.createElement("video"); video.src = videoItem.best_download_url; video.controls = true; video.style.cssText = [ "width:100% !important", "border-radius:12px !important", "margin-top:8px !important" ].join(";"); videoCard.appendChild(coverImg); videoCard.appendChild(video); mask.appendChild(videoCard); }); } else { pageTitle.textContent = "📝 纯文字推文"; const tip = document.createElement("div"); tip.style.cssText = [ "color:#999 !important", "text-align:center !important", "padding:30px 0 !important", "font-family:Arial,Helvetica,sans-serif !important" ].join(";"); tip.textContent = "这条推文无视频资源"; mask.appendChild(tip); } 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 updateInfo = JSON.parse(res.responseText); if (versionIsHigher(updateInfo.version, currentVersion) && updateInfo.forceUpdate) { alert(textUpdate); if (updateInfo.updateUrl) { window.open(updateInfo.updateUrl); } return; } startAnalyze(); } catch (err) { startAnalyze(); } }, onerror: startAnalyze, ontimeout: startAnalyze }); }; setInterval(toggleBtn, 800); } init(); })();