Greasy Fork

Greasy Fork is available in English.

Tiktok Video Downloader🎬

Download TikTok videos without watermark

目前为 2024-09-12 提交的版本。查看 最新版本

// ==UserScript==
// @name         Tiktok Video Downloader🎬
// @namespace    http://greasyfork.icu/en/scripts/431826
// @version      2.1
// @description  Download TikTok videos without watermark
// @author       YAD
// @match        *://*.tiktok.com/*
// @grant        GM_xmlhttpRequest
// @grant        GM_download
// @icon         https://miro.medium.com/v2/resize:fit:512/1*KX6NTUUHWlCP4sCXz28TBA.png
// @license      MIT
// ==/UserScript==

(function () {
    'use strict';

    const getFileName = (url) => {
        const videoId = url.split('/').pop().split('?')[0];
        return `TikTok_Video_${videoId}.mp4`;
    };

    const downloadVideo = (url, button) => {
        if (!url) {
            button.textContent = '✖️';
            return;
        }

        button.textContent = '⏳';
        GM_xmlhttpRequest({
            method: 'GET',
            url,
            responseType: 'blob',
            onload: ({ response, status, statusText }) => {
                if (response) {
                    GM_download({
                        url: URL.createObjectURL(response),
                        name: getFileName(url),
                        onload: () => {
                            button.textContent = '✔️';
                            setTimeout(() => button.remove(), 2000);
                        },
                        onerror: () => {
                            button.textContent = '✖️';
                            setTimeout(() => button.remove(), 1500);
                        }
                    });
                } else {
                    button.textContent = '✖️';
                    setTimeout(() => button.remove(), 1500);
                }
            },
            onerror: (error) => {
                button.textContent = '✖️';
                setTimeout(() => button.remove(), 1500);
            },
            ontimeout: () => {
                button.textContent = '✖️';
                setTimeout(() => button.remove(), 1500);
            }
        });
    };

    const createDownloadButton = (video) => {
        const button = document.createElement('div');
        Object.assign(button.style, {
            position: 'absolute',
            right: '15px',
            top: '27%',
            transform: 'translateY(-50%)',
            width: '50px',
            height: '50px',
            backgroundColor: '#ff3b5c',
            color: '#ffffff',
            fontSize: '18px',
            textShadow: '3px 3px 0px #9C1331',
            textAlign: 'center',
            lineHeight: '50px',
            borderRadius: '50%',
            cursor: 'pointer',
            zIndex: '1000'
        });
        button.textContent = '🎞️';

        button.onclick = async (e) => {
            e.stopPropagation();
            e.preventDefault();
            button.textContent = '⏳';
            const videoUrl = video.src || video.querySelector('source')?.src;

            if (videoUrl && videoUrl.startsWith('blob:')) {
                button.style.backgroundColor = '#ffc107';

                const xgwrapper = document.querySelector('[id^="xgwrapper-"]');
                const videoId = xgwrapper?.id.split('-')[2];
                const tiktokVideoUrl = `https://www.tiktok.com/@YAD/video/${videoId}`;

                const iframe = document.createElement('iframe');
                iframe.style.position = 'fixed';
                iframe.style.visibility = 'hidden';
                iframe.style.top = '20px';
                iframe.style.left = '5px';
                iframe.style.border = 'none';
                iframe.src = tiktokVideoUrl;
                document.body.appendChild(iframe);

                const muteVideoInIframe = () => {
                    const iframeDocument = iframe.contentDocument || iframe.contentWindow.document;
                    const videoElement = iframeDocument.querySelector('video');
                    if (videoElement) {
                        videoElement.muted = true;
                    }
                };

                const checkVideoUrl = () => {
                    const iframeDocument = iframe.contentDocument || iframe.contentWindow.document;
                    const videoElement = iframeDocument.querySelector('video');
                    if (videoElement && !videoElement.src.startsWith('blob:')) {
                        muteVideoInIframe();
                        downloadVideo(videoElement.src, button);
                        iframe.remove();
                    } else {
                        setTimeout(checkVideoUrl, 1000);
                    }
                };

                setTimeout(checkVideoUrl, 6000);
            } else {
                downloadVideo(videoUrl, button);
            }
        };

        video.parentNode.style.position = 'relative';
        video.parentNode.appendChild(button);

        return button;
    };

    const manageDownloadButtons = (video) => {
        let button;
        video.addEventListener('mouseover', () => {
            if (!button) {
                button = createDownloadButton(video);
            }
        });
        video.addEventListener('mouseout', (e) => {
            if (button && !video.contains(e.relatedTarget) && !button.contains(e.relatedTarget)) {
                button.remove();
                button = null;
            }
        });
    };

    const checkForNewVideos = () => {
        document.querySelectorAll('video:not(.processed)').forEach((video) => {
            video.classList.add('processed');
            manageDownloadButtons(video);
        });
        requestAnimationFrame(checkForNewVideos);
    };

    requestAnimationFrame(checkForNewVideos);

})();