Twitter/X(网页版)视频/图片/gif一键下载.[limbopro]

Twitter 网页版视频/gif下载(GIF/单/多视频解析及下载)

目前為 2024-12-03 提交的版本,檢視 最新版本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==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/gifs download.[limbopro]
// @name:ko     Twitter/X(웹버전) 동영상/사진/gif 원클릭 다운로드.[limbopro]
// @name:ru     Twitter/X (веб-версия) — загрузка видео/изображений/гифок в один клик.[limbopro]
// @namespace    https://limbopro.com/
// @version      0.1.3.11
// @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/gifs 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;} .download_pics{} .house {display:flex; flex-direction:row; margin-top:10px;}.help{top:80px !important;} .twdl {text-decoration:none; position:sticky; top:5px; /*text-transform:uppercase;*/ padding:6px 12px; color:white; z-index:114154; padding-right:15px;} .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;} .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 regex_status = 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(regex_status)) {
            //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 regex_status = new RegExp(/^https:\/\/x\.com\/.*?\/status\/\d{10,100}$/gi) // 正则匹配对的 Tweet url
    article.querySelectorAll('a').forEach((x) => { // 获取 twitter url
        if (x.href.match(regex_status)) {
            twdl_Kurl = x.href
        }
    })
    console.log('当前推文链接🔗...' + ' ' + twdl_Kurl)
    return twdl_Kurl;
}

function alert_ifnopics() {
    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 loader(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 these videos/imgs/Gifs via " + x;
                return textContent;
                break;
            case 'ru':
                textContent = "Загрузите эти видео/изображения/анимацию через " + x;
                return textContent;
                break;
            default:
                textContent = "Download these videos/imgs/Gifs 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 these videos via " + x;
                return textContent;
                break;
            case 'ru':
                textContent = "Загрузите эти видео через" + x;
                return textContent;
                break;
            default:
                textContent = "Download these videos via " + x;
                return textContent;
                break;
        }
    }

}

function batch_loader() { // [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 = 'Bulk download imgs';
            return textContent;
            break;
        case 'ru':
            textContent = "Загрузка изображений партиями";
            return textContent;
            break;
        default:
            textContent = 'Bulk download imgs';
            return textContent;
            break;
    }
}


function dlpics(imgsrc, name) {

    if (imgsrc.length == 0) {
        alert(alert_ifnopics())
    } else {

        console.log("get_url_imgs " + name + " by fn dlpics...")
        // code written by CodeingShare 
        // https://ww4k.com/CodeingShare/donwload_image_difference_domain.html
        // 解决跨域 Canvas 污染问题
        imgsrc.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 = name || "photo";
                a.href = url;
                a.dispatchEvent(event);
            };
            image.src = x;
        })
    }
}


function get_url_imgs(article, userName) {
    var url = [];
    article.querySelectorAll('a[class=' + userName + ']').forEach((x) => {
        url.push(x)
    })
    console.log("get_url_imgs " + userName)
    console.log("get_url_imgs " + url.length + "张...")
    return url;
}

function userName(article) {
    var fileName = '';
    var regex_name = new RegExp(/\/status\/\d{10,100}$/gi) // 正则匹配对的 Tweet url
    var regex_status = new RegExp(/^https:\/\/x\.com\/.*?\/status\/\d{10,100}$/gi) // 正则匹配对的 Tweet url

    article.querySelectorAll('a').forEach((x) => { // 获取 twitter url
        if (x.href.match(regex_status)) {
            fileName = x.href.replaceAll('https://x.com/', '').replaceAll(regex_name, '')
        }
    })

    return fileName;
}

function god() {
    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', loader('[VID]'))
                var loader_ = twdl_div(article[i], 'https://twittervideodownloader.com/', 'twdl twittervideodownloader', loader('[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 = batch_loader()

                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', () => {
                    dlpics(get_url_imgs(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(() => {
    god()
}, 4000)

/* })(); */

function download_xvideos() { // 获取当前网页 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(regex_status)) {
                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(regex_status)) {
                document.querySelector('#submitBtn').click()
            }
        }
    }

}

if (window.location.href.match(/(twittervid\.com|twittervideodownloader)/gi) !== null) {
    download_xvideos()
}