// ==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: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
// @description Twitter 网页版视频/gif下载(GIF/单/多视频解析及下载)
// ==/UserScript==
/*
@ author: limbopro
@ website: http://limbopro.com/
@ Gmail: [email protected]
@ 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()
}