// ==UserScript== // @name Twitter/X(网页版)视频/图片/gif一键下载.[limbopro] // @name:ja Twitter/X (Web 版) のビデオ/写真/GIF をワンクリックでダウンロード。[limbopro] // @name:zh-cn Twitter/X(网页版)视频/图片/gif一键下载.[limbopro] // @name:zh-tw Twitter/X(網頁版)影片/圖片/gif一鍵下載.[limbopro] // @name:en Twitter/X(web version)videos/pictures/gif download.[limbopro] // @name:ko Twitter/X(웹버전) 동영상/사진/gif 원클릭 다운로드.[limbopro] // @name:ru Twitter/X (веб-версия) — загрузка видео/изображений/гифок в один клик.[limbopro] // @namespace https://limbopro.com/ // @version 0.1.3.12 // @description Twitter/X(网页版)视频/图片/gif一键下载.[limbopro] / 一键下载推文图片并按用户名进行保存 // @description:zh-cn Twitter/X(网页版)视频/图片/gif一键下载.[limbopro] / 一键下载推文图片并按用户名进行保存 // @description:ja Twitter/X (Web 版) のビデオ/写真/GIF をワンクリックでダウンロード。[limbopro] / ワンクリックでツイート画像をダウンロードし、ユーザー名で保存します // @description:zh-tw Twitter/X(網頁版)影片/圖片/gif一鍵下載.[limbopro] / 一鍵下載推文圖片並按使用者名稱儲存 // @description:en Twitter/X(web version)videos/pictures/gif download.[limbopro] / Download tweet images with one click and save by username // @description:ru Twitter/X (веб-версия) — загрузка видео/изображений/гифок в один клик.[limbopro] / Загрузите изображения твитов одним щелчком мыши и сохраните их по имени пользователя. // @description:ko Twitter/X(웹버전) 동영상/사진/gif 원클릭 다운로드.[limbopro] / 한 번의 클릭으로 트윗 이미지를 다운로드하고 사용자 이름으로 저장 // @author limbopro // @license MIT // @match https://twitter.com/* // @match https://x.com/* // @match https://twittervideodownloader.com/* // @match https://twittervid.com/* // @icon https://www.google.com/s2/favicons?sz=64&domain=twitter.com // @grant none // @downloadURL none // ==/UserScript== /* @ author: limbopro @ website: http://limbopro.com/ @ Gmail: service.limbopro.com@gmail.com @ Github: https://github.com/limbopro @ X: https://x.com/limboprossr */ /* (function () { 'use strict'; */ // 引入全局 CSS var twdlcss = "button.twdl.download_pics:hover {background-color: #f038ff;-webkit-box-shadow: 10px 10px 99px 6px rgba(240, 56, 255, 1);-moz-box-shadow: 10px 10px 99px 6px rgba(240, 56, 255, 1);box-shadow: 10px 10px 99px 6px rgba(240, 56, 255, 1);;transition: 0.7s;} .atx {display:none;} .house {/*max-width:333px;*/display:flex; flex-direction:row; /*flex-wrap:wrap;*/ margin-top:5px;}.help{top:80px !important;/*background:teal;*/} .twdl {line-height:normal; font-size:xx-small; text-decoration:none; position:sticky; top:5px; /*text-transform:uppercase;*/ padding:6px 12px; color:white; z-index:114154;} .twittervideodownloader {top:40px; background:linear-gradient(to bottom, #42a5f5 0%, #1e88e5 100%); box-shadow:inset 0 2px 2px #1976d2;} .twittervid {background:linear-gradient(to bottom, #66BB6A 0%, #43A047 100%); box-shadow:inset 0 2px 2px #388E3C;} .download_pics { /*border-radius:5px 0px 0px 5px; */ border:0px;} .greasyfork {cursor:help; right:295px;background:linear-gradient(rgb(62 53 53) 0%, rgb(31 29 29) 100%);box-shadow:rgb(0 0 0) 0px 2px 2px inset;}" var newstyle = document.createElement('style') newstyle.id = 'twdlcss' newstyle.innerHTML = twdlcss document.querySelector('head').parentNode.insertBefore(newstyle, document.querySelector('head')) // 载入 var twURL_regex = new RegExp(/^https:\/\/x\.com\/.*?\/status\/\d{10,100}$/gi) // 正则匹配对的 Tweet url function twdl_div(article, downloaderURL, className, textContent) { // article = article[i] let a = document.createElement('a') article.querySelectorAll('a').forEach((x) => { // 获取 twitter url if (x.href.match(twURL_regex)) { //console.log(x.href); a.href = downloaderURL + "#" + x.href; //console.log(a.href) } }) a.className = className; a.target = '_blank'; a.zIndex = '114154'; a.textContent = textContent; return a; } function twdl_url(article) { var twdl_Kurl = ''; var twURL_regex = new RegExp(/^https:\/\/x\.com\/.*?\/status\/\d{10,100}$/gi) // 正则匹配对的 Tweet url article.querySelectorAll('a').forEach((x) => { // 获取 twitter url if (x.href.match(twURL_regex)) { twdl_Kurl = x.href } }) console.log('当前推文链接🔗...' + ' ' + twdl_Kurl) return twdl_Kurl; } function iftwnopics_innerText() { var language = document.querySelector('html').lang; // en/ja/zh/ru/zh-Hant var textContent = ''; switch (language) { // case 'zh': textContent = "该推文内容不存在图片!"; return textContent; break; case 'zh-Hant': textContent = "該推文內容不存在圖片!"; return textContent; break; /* case 'ja': textContent = "このツイートには画像がありません!"; return textContent; break; */ case 'en': textContent = "There is no image in this tweet!"; return textContent; break; /* case 'ru': textContent = "В этом твите нет изображения!"; return textContent; break; */ default: textContent = "There is no image in this tweet!"; return textContent; break; } } function downloader_innerText(x) { // [LOADER]/[VID] // 判断当前网页语言 var language = document.querySelector('html').lang; // en/ja/zh/ru/zh-Hant var textContent = ''; if (x == '[VID]') { switch (language) { // case 'zh': textContent = "通过" + x + "下载视频/动图"; return textContent; break; case 'zh-Hant': textContent = "透過" + x + "下載影片/動圖"; return textContent; break; /* case 'ja': textContent = "これらのビデオ/写真/アニメーションを" + x + "経由でダウンロードしてください"; return textContent; break; */ case 'en': textContent = "Download video/img/gif via " + x; return textContent; break; /* case 'ru': textContent = "Загрузите эти видео/изображения/анимацию через " + x; return textContent; break; */ default: textContent = "Download video/img/gif via " + x; return textContent; break; } } else if (x == '[LOADER]') { switch (language) { // case 'zh': textContent = "通过" + x + "下载视频"; return textContent; break; case 'zh-Hant': textContent = "透過" + x + "下載影片"; return textContent; break; /* case 'ja': textContent = x + "経由でビデオをダウンロード"; return textContent; break; */ case 'en': textContent = "Download video via " + x; return textContent; break; /* case 'ru': textContent = "Скачать видео через " + x; return textContent; break; */ default: textContent = "Download video via " + x; return textContent; break; } } } function dlpics_innerText() { // [LOADER]/[VID] // 判断当前网页语言 var language = document.querySelector('html').lang; // en/ja/zh/ru/zh-Hant var textContent = ''; switch (language) { // case 'zh': textContent = "下载图片"; return textContent; break; case 'zh-Hant': textContent = "下載圖片"; return textContent; break; /* case 'ja': textContent = "写真をダウンロードする"; return textContent; break; */ case 'en': textContent = 'Download img'; return textContent; break; /* case 'ru': textContent = "Скачать картинки"; return textContent; break; */ default: textContent = 'Download img'; return textContent; break; } } function dlpicsfromURL(imgsrcURL, userName) { if (imgsrcURL.length == 0) { alert(iftwnopics_innerText()) } else { console.log("get_imgsURL " + userName + " by fn dlpicsfromURL...") // code written by CodeingShare // https://ww4k.com/CodeingShare/donwload_image_difference_domain.html // 解决跨域 Canvas 污染问题 imgsrcURL.forEach((x) => { var image = new Image(); image.setAttribute("crossOrigin", "anonymous"); image.onload = function () { var canvas = document.createElement("canvas"); canvas.width = image.width; canvas.height = image.height; var context = canvas.getContext("2d"); context.drawImage(image, 0, 0, image.width, image.height); var url = canvas.toDataURL("image/png"); var a = document.createElement("a"); var event = new MouseEvent("click"); a.download = userName || "photo"; a.href = url; a.dispatchEvent(event); }; image.src = x; }) } } function get_imgsURL(article, userName) { var url = []; article.querySelectorAll('a[class=' + userName + ']').forEach((x) => { url.push(x) }) console.log("get_imgsURL " + userName) console.log("get_imgsURL " + url.length + "张...") return url; } function userName(article) { var fileName = ''; var regex_name = new RegExp(/\/status\/\d{10,100}$/gi) // 正则匹配对的 Tweet url var twURL_regex = new RegExp(/^https:\/\/x\.com\/.*?\/status\/\d{10,100}$/gi) // 正则匹配对的 Tweet url article.querySelectorAll('a').forEach((x) => { // 获取 twitter url if (x.href.match(twURL_regex)) { fileName = x.href.replaceAll('https://x.com/', '').replaceAll(regex_name, '') } }) return fileName; } function twdl() { if (document.querySelectorAll('[data-testid="cellInnerDiv"]')) { var article = document.querySelectorAll('[data-testid="cellInnerDiv"]') for (let i = 0; i < article.length; i++) { // twittervid if (!article[i].querySelector('a[href*=greasyfork]')) { var house = document.createElement('div') house.className = 'house' var vid = twdl_div(article[i], 'https://twittervid.com/', 'twdl twittervid', downloader_innerText('[VID]')) var loader_ = twdl_div(article[i], 'https://twittervideodownloader.com/', 'twdl twittervideodownloader', downloader_innerText('[LOADER]')) var help = twdl_div(article[i], 'https://greasyfork.org/zh-CN/scripts/478651-twitter-%E7%BD%91%E9%A1%B5%E7%89%88%E5%A4%9A%E8%A7%86%E9%A2%91-gif%E4%B8%8B%E8%BD%BD-limbopro', 'twdl help', 'Need Help?') var downloader = document.createElement('button') downloader.className = 'twdl download_pics' downloader.innerText = dlpics_innerText() article[i].querySelectorAll("img[src*='name=']").forEach((x) => { var a = document.createElement('a') a.href = x.src a.className = "twdl_" + userName(article[i]) house.appendChild(a) }) var array = [downloader, vid, loader_, help] array.forEach((x) => { house.appendChild(x) }) if (article[i].querySelectorAll("div.css-175oi2r.r-12kyg2d")[0] && article[i].querySelector('[data-testid="videoPlayer"]')) { // 推文存在文字图片且有视频的情况下 article[i].querySelectorAll("div.css-175oi2r.r-12kyg2d")[0].appendChild(house); console.log('追加下载按钮->' + userName(article[i])) console.log("推文链接🔗-> " + twdl_url(article[i])) } else if (article[i].querySelectorAll('[dir=auto][lang]')[0] && article[i].querySelector('[data-testid="videoPlayer"]')) { article[i].querySelectorAll('[dir=auto][lang]')[0].appendChild(house); console.log('追加下载按钮' + "该推文存在视频与文字-> " + userName(article[i])) console.log("推文链接🔗-> " + twdl_url(article[i])) } else if (article[i].querySelector('[data-testid="videoPlayer"]')) { // 推文没有文字图片仅有视频的情况下 article[i].querySelector("[data-testid='videoComponent']").appendChild(house) console.log('追加下载按钮' + "该推文只存在视频-> " + userName(article[i])) console.log("推文链接🔗-> " + twdl_url(article[i])) } else if (article[i].querySelectorAll('[dir=auto][lang]')[0] && article[i].querySelectorAll("img[src*='name=']").length >= 1) { article[i].querySelectorAll('[dir=auto][lang]')[0].appendChild(house); console.log('追加下载按钮' + "该推文存在图片-> " + userName(article[i])) } else if (article[i].querySelectorAll("img[src*='name=']").length >= 1 && article[i].querySelectorAll("img")[1] !== null) { article[i].querySelectorAll("div[aria-labelledby]")[0].parentNode.insertBefore(house, article[i].querySelectorAll("div[aria-labelledby]")[0]) console.log('追加下载按钮' + "该推文只存在图片-> " + userName(article[i])) } downloader.addEventListener('click', () => { dlpicsfromURL(get_imgsURL(article[i], "twdl_" + userName(article[i])), userName(article[i])) }) console.log('house username: ' + userName(article[i])) console.log("house url " + twdl_url(article[i])) console.log('house ...') } } } } setInterval(() => { twdl() }, 4000) /* })(); */ function inDownloaderPage() { // 获取当前网页 url -> 给 input 赋值 -> 点击下载按钮 if (window.location.href.match(/(twittervid\.com)/gi)) { if (document.querySelector('#tweetUrl') !== null && document.querySelector('#loadVideos') !== null) { document.querySelector('#tweetUrl').value = window.location.href.replace('https://twittervid.com/#', '') if (document.querySelector('#tweetUrl').value == 'https://twittervid.com/') { } else if (document.querySelector('#tweetUrl').value.match(twURL_regex)) { document.querySelector('#loadVideos').click() } } } if (window.location.href.match(/(twittervideodownloader\.com)/gi)) { if (document.querySelector('#tweetURL') !== null && document.querySelector('#submitBtn') !== null) { document.querySelector('#tweetURL').value = window.location.href.replace('https://twittervideodownloader.com/#', '') if (document.querySelector('#tweetURL').value == 'https://twittervideodownloader.com/') { } else if (document.querySelector('#tweetUrl').value.match(twURL_regex)) { document.querySelector('#submitBtn').click() } } } } if (window.location.href.match(/(twittervid\.com|twittervideodownloader)/gi) !== null) { inDownloaderPage() }