吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 2472|回复: 26
收起左侧

[其他原创] 【油猴脚本】Tiktok视频下载(支持主页)

  [复制链接]
Webrobot 发表于 2024-9-25 20:56
本帖最后由 Webrobot 于 2024-9-26 19:25 编辑

支持单视频以及主页所有视频下载,HD画质
访问用户主页时,右下角会出现下载按钮
访问单视频时,播放界面会显示下载按钮
自行复制代码到油猴中新建脚本保存即可


[JavaScript] 纯文本查看 复制代码
// ==UserScript==
// @name         Tiktok视频下载
// @namespace    tiktok_video_download
// @version      1.0
// @description  解析和下载Tiktok视频(无水印)
// @AuThor       Webrobot
// @match         *://*.tiktok.com/*
// @grant        noneGM_openInTab
// @grant             GM_xmlhttpRequest
// @grant             GM_setValue
// @grant             GM_getValue
// @grant             GM_addStyle
// @run-at            document-idle
// ==/UserScript==
 
(function() {    'use strict';

    // 通用方法定义
    const commonFunctionObject = {
        GMopenInTab: function (url, options = { active: true, insert: true, setParent: true }) {
            if (typeof GM_openInTab === "function") {
                GM_openInTab(url, options);
            }
        },
        GMaddStyle: function (css) {
            const style = document.createElement('style');
            style.textContent = css;
            document.head.appendChild(style);
        },
        webToast: function (params) {
            const { message, background } = params;
            const toast = document.createElement('div');
            toast.style.position = 'fixed';
            toast.style.bottom = '20px';
            toast.style.left = '50%';
            toast.style.transform = 'translateX(-50%)';
            toast.style.background = background || 'rgba(0, 0, 0, 0.7)';
            toast.style.color = '#fff';
            toast.style.padding = '10px';
            toast.style.borderRadius = '5px';
            toast.style.zIndex = '99999';
            toast.innerText = message;
            document.body.appendChild(toast);
            setTimeout(() => document.body.removeChild(toast), 1500);
        },
        request: function (method, url, data, headers = { "Content-Type": "application/json;charset=UTF-8" }) {
            return new Promise((resolve, reject) => {
                GM_xmlhttpRequest({
                    url: url,
                    method: method,
                    data: data,
                    headers: headers,
                    onload: function (response) {
                        if (response.status === 200) {
                            resolve({ result: 'success', data: response.responseText });
                        } else {
                            reject({ result: 'error', data: null });
                        }
                    }
                });
            });
        }
    };

    function Tiktok() {
        this.extractHref = function (html) {
            const regex = /<a\s+(?:[^>]*?\s+)?href=(['"])(.*?)\1/gi;
            const hrefs = [];
            let match;
            while ((match = regex.exec(html)) !== null) {
                hrefs.push(match[2]);
            }
            return hrefs.filter((href) => href.indexOf("snapcdn.app") != -1);
        };

        this.extractAllVideoLinks = function () {
            const videoLinks = [];
            const anchorTags = document.querySelectorAll('a');
            anchorTags.forEach(anchor => {
                const href = anchor.getAttribute('href');
                if (href && /^https:\/\/www\.tiktok\.com\/@[^/]+\/video\/\d+/.test(href)) {
                    videoLinks.push(href);
                }
            });
            return videoLinks;
        };

        this.scrollToBottom = async function () {
            return new Promise((resolve) => {
                let previousHeight = 0;
                const scrollInterval = setInterval(() => {
                    window.scrollTo(0, document.body.scrollHeight);
                    if (document.body.scrollHeight !== previousHeight) {
                        previousHeight = document.body.scrollHeight;
                    } else {
                        clearInterval(scrollInterval);
                        resolve();
                    }
                }, 1000);
            });
        };

        this.downloadSingleVideo = async function (url, element) {
            commonFunctionObject.webToast({ "message": "正在下载中.", "background": "#000" });
            element.classList.add("download-loadding");

            const data = await commonFunctionObject.request("POST", "https://tikdownloader.io/api/ajaxSearch",
                "q=" + url + "&lang=en", { "Content-Type": "application/x-www-form-urlencoded;charset=UTF-8" });
            if (data.result === "success") {
                const result = JSON.parse(data.data);
                if (result.status == "ok" && result.hasOwnProperty("data")) {
                    const data = result.data;
                    const downloadUrls = this.extractHref(data);
                    if (downloadUrls.length >= 2) {
                        commonFunctionObject.GMopenInTab(downloadUrls.at(-2));
                    }
                }
            }

            element.classList.remove("download-loadding");
        };

        this.downloadAllVideos = async function (element) {
            commonFunctionObject.webToast({ "message": "正在加载所有视频,请稍候...", "background": "#000" });
            element.classList.add("download-loadding");

            await this.scrollToBottom();  // 先滚动到页面底部,加载所有视频

            const videoLinks = this.extractAllVideoLinks();
            if (videoLinks.length === 0) {
                commonFunctionObject.webToast({ "message": "未找到任何视频链接", "background": "#000" });
                element.classList.remove("download-loadding");
                return;
            }

            commonFunctionObject.webToast({ "message": "开始下载所有视频中...", "background": "#000" });
            for (const videoLink of videoLinks) {
                const data = await commonFunctionObject.request("POST", "https://tikdownloader.io/api/ajaxSearch",
                    "q=" + videoLink + "&lang=en", { "Content-Type": "application/x-www-form-urlencoded;charset=UTF-8" });
                if (data.result === "success") {
                    const result = JSON.parse(data.data);
                    if (result.status == "ok" && result.hasOwnProperty("data")) {
                        const data = result.data;
                        const downloadUrls = this.extractHref(data);
                        if (downloadUrls.length >= 2) {
                            commonFunctionObject.GMopenInTab(downloadUrls.at(-2));
                        }
                    }
                }
            }

            element.classList.remove("download-loadding");
        };

        this.start = async function () {
            this.removeButtons();
            const currentUrl = window.location.href;
            if (/www\.tiktok\.com\/@[^/]+$/.test(currentUrl)) {
                this.addBatchDownloadButton();
            } else if (/www\.tiktok\.com\/@[^/]+\/video\/\d+/.test(currentUrl)) {
                this.addSingleDownloadButton();
            }
        };

        this.addBatchDownloadButton = function () {
            commonFunctionObject.GMaddStyle(`
                @keyframes scriptspin {0% {transform: rotate(0deg);} 100% {transform: rotate(360deg);}}
                .download-loadding{
                    animation: scriptspin 1s linear infinite;
                }
                #tiktok-download-990i {
                    position: fixed;
                    bottom: 20px;
                    right: 20px;
                    background: #ff2d55;
                    border-radius: 50%;
                    padding: 10px;
                    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
                    cursor: pointer;
                    z-index: 9999;
                    display: flex;
                    align-items: center;
                    justify-content: center;
                }
                #tiktok-download-990i svg {
                    fill: #ffffff;
                }
            `);

            if (!document.querySelector("#tiktok-download-990i")) {
                const container = document.querySelector('body');
                if (!container) {
                    return;
                }

                let downloadButton = document.createElement('div');
                downloadButton.id = "tiktok-download-990i";
                downloadButton.innerHTML = `<svg t="1724300009050" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5307" width="35" height="35"><path d="M298.666667 554.666667v85.333333H256v128h512v-128h-42.666667v-85.333333h128v213.333333a85.333333 85.333333 0 0 1-78.933333 85.077333L768 853.333333H256a85.333333 85.333333 0 0 1-85.12-78.933333L170.666667 768v-213.333333h128z" fill="#ffffff" p-id="5308"></path><path d="M512 627.498667l219.477333-219.477334h-120.704L512 506.88 413.141333 408.021333H292.522667L512 627.498667z" fill="#ffffff" p-id="5309"></path><path d="M554.666667 528V167.978667h-85.333334v360.021333h85.333334z" fill="#ffffff" p-id="5310"></path></svg>`;
                downloadButton.title = "点击下载所有视频(高清无水印)";
                downloadButton.addEventListener("click", () => {
                    this.downloadAllVideos(downloadButton);
                });

                container.appendChild(downloadButton);
            }
        };

        this.addSingleDownloadButton = function () {
            commonFunctionObject.GMaddStyle(`
                @keyframes scriptspin {0% {transform: rotate(0deg);} 100% {transform: rotate(360deg);}}
                .download-loadding{
                    animation: scriptspin 1s linear infinite;
                }
            `);

            if (!document.querySelector("#tiktok-download-single-990i")) {
                const container = document.querySelector('#main-content-video_detail') || document.body;
                if (!container) {
                    return;
                }

                const divs = container.querySelectorAll("div");
                const regex = /-DivRightControlsWrapper|-DivMiniPlayerContainer/;
                const matchedDiv = Array.from(divs).find(div => {
                    return div.classList.value.split(' ').some(className => {
                        return regex.test(className);
                    });
                });

                if (matchedDiv) {
                    let cloneNode = null;
                    let isDetail = matchedDiv.children.length != 1;
                    if (isDetail) {
                        cloneNode = matchedDiv.children[0].cloneNode(true);
                    } else {
                        cloneNode = matchedDiv.cloneNode(true);
                    }
                    cloneNode.id = "tiktok-download-single-990i";
                    cloneNode.querySelector("div").innerHTML = `<svg t="1724300009050" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5307" width="35" height="35"><path d="M298.666667 554.666667v85.333333H256v128h512v-128h-42.666667v-85.333333h128v213.333333a85.333333 85.333333 0 0 1-78.933333 85.077333L768 853.333333H256a85.333333 85.333333 0 0 1-85.12-78.933333L170.666667 768v-213.333333h128z" fill="#ffffff" p-id="5308"></path><path d="M512 627.498667l219.477333-219.477334h-120.704L512 506.88 413.141333 408.021333H292.522667L512 627.498667z" fill="#ffffff" p-id="5309"></path><path d="M554.666667 528V167.978667h-85.333334v360.021333h85.333334z" fill="#ffffff" p-id="5310"></path></svg>`;
                    if (isDetail) {
                        matchedDiv.insertBefore(cloneNode, matchedDiv.children[0]);
                    } else {
                        cloneNode.style.right = (166) + "px";
                        matchedDiv.parentNode.insertBefore(cloneNode, matchedDiv);
                    }
                    cloneNode.title = "点击下载视频(高清无水印)";
                    cloneNode.addEventListener("click", () => {
                        this.downloadSingleVideo(window.location.href, cloneNode);
                    });
                }
            }
        };

        this.removeButtons = function () {
            const batchButton = document.querySelector("#tiktok-download-990i");
            if (batchButton) {
                batchButton.remove();
            }

            const singleButton = document.querySelector("#tiktok-download-single-990i");
            if (singleButton) {
                singleButton.remove();
            }
        };

        this.observeUrlChange = function () {
            let previousUrl = window.location.href;
            const observer = new MutationObserver(() => {
                if (previousUrl !== window.location.href) {
                    previousUrl = window.location.href;
                    this.start();
                }
            });
            const config = { subtree: true, childList: true };
            observer.observe(document, config);
        };
    }

    try {
        const tiktok = new Tiktok();
        tiktok.start();
        tiktok.observeUrlChange();
    } catch (e) {
        console.log("Tiktok视频下载脚本出错:" + e);
    }
})();

免费评分

参与人数 4吾爱币 +10 热心值 +4 收起 理由
502A5241 + 1 + 1 谢谢@Thanks!
苏紫方璇 + 7 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
rectdbszy + 1 + 1 谢谢@Thanks!
hitlerboy + 1 + 1 我很赞同!

查看全部评分

本帖被以下淘专辑推荐:

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

826384168 发表于 2024-9-25 23:52
莫奇 发表于 2024-9-25 23:45
一样的提示

[Asm] 纯文本查看 复制代码
// ==UserScript==
// @name         Tiktok视频下载
// @namespace    tiktok_video_download
// @version      1.0
// @description  解析和下载Tiktok视频(无水印)
// @AuThor       Webrobot
// @match         *://*.tiktok.com/*
// @Icon         https://lf1-cdn-tos.bytegoofy.com/goofy/ies/douyin_web/public/favicon.ico
// @grant        noneGM_openInTab
// @grant             GM_xmlhttpRequest
// @grant             GM_setValue
// @grant             GM_getValue
// @grant             GM_addStyle
// @run-at            document-idle
// ==/UserScript==

(function() {


前面几行改成这样就可以保存了
goodrain 发表于 2024-9-25 22:20
happyxj 发表于 2024-9-25 22:25
复制保存后,提示脚本无效呀,楼主请帮忙看看撒,谢谢
826384168 发表于 2024-9-25 23:31
happyxj 发表于 2024-9-25 22:25
复制保存后,提示脚本无效呀,楼主请帮忙看看撒,谢谢

我也是,不知道啥情况
莫奇 发表于 2024-9-25 23:45
happyxj 发表于 2024-9-25 21:25
复制保存后,提示脚本无效呀,楼主请帮忙看看撒,谢谢

一样的提示
SWH888 发表于 2024-9-25 23:54
用IDM能下吗
莫奇 发表于 2024-9-25 23:54
826384168 发表于 2024-9-25 22:52
[mw_shl_code=asm,true]// ==UserScript==
// @name         Tiktok视频下载
// @namespace    tiktok_ ...

代码问题?
cclnnl 发表于 2024-9-26 01:25
谢谢楼主分享
chtjap 发表于 2024-9-26 01:38
提示脚本无效呀,楼主请帮忙看看撒,谢谢
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-12-11 20:40

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表