Greasy Fork

Greasy Fork is available in English.

Tiktok Video Downloader🎬

Download TikTok videos without watermark

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

// ==UserScript==
// @name         Tiktok Video Downloader🎬
// @namespace    http://greasyfork.icu/en/scripts/431826
// @version      1.5
// @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
// ==/UserScript==

(function() {
    'use strict';

    const INTERVAL_MS = 1000;

    // Extract video ID from the URL to name the file
    const getFileName = (url) => {
        const videoId = url.split('/').pop().split('?')[0];
        return `TikTok_Video_${videoId}.mp4`;
    };

    // Function to handle video download with error handling and feedback
    const downloadVideo = (url, button) => {
        if (!url) {
            console.error('No valid video URL found.');
            button.textContent = '✖️';
            return;
        }

        button.textContent = '⏳';
        GM_xmlhttpRequest({
            method: 'GET',
            url,
            responseType: 'blob',
            onload: ({ response, status, statusText }) => {
                console.log(`Response Status: ${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(), 2000);
                        }
                    });
                } else {
                    console.error('Failed to download the video.');
                    button.textContent = '✖️';
                    setTimeout(() => button.remove(), 2000);
                }
            },
            onerror: (error) => {
                console.error('An error occurred during the request.', error);
                button.textContent = '✖️';
                setTimeout(() => button.remove(), 2000);
            },
            ontimeout: () => {
                console.error('Request timed out.');
                button.textContent = '✖️';
                setTimeout(() => button.remove(), 2000);
            }
        });
    };

    // Create and style the download button
    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 #4C0F1D',
            textAlign: 'center',
            lineHeight: '50px',
            borderRadius: '50%',
            cursor: 'pointer',
            zIndex: '1000'
        });
        button.textContent = '🎞️';

        // On button click, initiate download and provide feedback
        button.onclick = (e) => {
            e.stopPropagation();
            e.preventDefault();
            button.style.backgroundColor = '#EF2950';
            const videoUrl = video.src || video.querySelector('source')?.src;
            downloadVideo(videoUrl, button);
        };

        // Append the button to the video container
        video.parentNode.style.position = 'relative';
        video.parentNode.appendChild(button);

        return button;
    };

    // Manage button visibility and creation on hover
    const manageDownloadButtons = (video) => {
        let button;
        video.addEventListener('mouseover', () => {
            if (!button) {
                button = createDownloadButton(video);
            }
        });
        // Remove the button only after download is complete or fails
        video.addEventListener('mouseout', (e) => {
            if (button && !video.contains(e.relatedTarget) && !button.contains(e.relatedTarget)) {
                button.remove();
                button = null;
            }
        });
    };

    // Function to periodically check for new videos
    const checkForNewVideos = () => {
        document.querySelectorAll('video:not(.processed)').forEach((video) => {
            video.classList.add('processed');
            manageDownloadButtons(video);
        });
    };

    // Start interval to periodically check for new videos
    setInterval(checkForNewVideos, INTERVAL_MS);

})();