TikTok Video Downloader - Ultimate

Download TikTok Videos Without A Watermark

נכון ליום 05-08-2024. ראה הגרסה האחרונה.

// ==UserScript==
// @name         TikTok Video Downloader - Ultimate
// @namespace    none
// @version      2.05
// @description  Download TikTok Videos Without A Watermark
// @author       altaireh
// @match        *://*.tiktok.com/*
// @icon         https://encrypted-tbn3.gstatic.com/images?q=tbn:ANd9GcRA-YRvXcW4n9Uv8FvTYubFk4uLqV2A4J___55paaZmd3y1TT8q
// @grant        none
// ==/UserScript==

(() => {
    'use strict';

    const CONFIG = {
        ICON_URL: 'https://encrypted-tbn3.gstatic.com/images?q=tbn:ANd9GcRA-YRvXcW4n9Uv8FvTYubFk4uLqV2A4J___55paaZmd3y1TT8q',
        SUCCESS_MESSAGE: 'Download Success!',
        CONSOLE_PREFIX: 'Altaireh TikTokVD'
    };

    // Display message with custom console prefix
    const displayMessage = (message) => {
        console.log(`%c${CONFIG.CONSOLE_PREFIX}%c: ${message}`, 'color: #1E90FF; font-weight: bold;', 'color: inherit;');
    };

    // Download video using an iframe
    const downloadVideo = (url) => {
        let cspErrorDetected = false;

        // Handle CSP violation
        const handleCSPViolation = () => {
            cspErrorDetected = true;
        };

        window.addEventListener('securitypolicyviolation', handleCSPViolation, { once: true });

        // Create and use iframe
        const iframe = document.createElement('iframe');
        iframe.style.display = 'none';
        iframe.src = url;
        document.body.appendChild(iframe);

        // Monitor for CSP violations and finalize after a short delay
        setTimeout(() => {
            document.body.removeChild(iframe);
            window.removeEventListener('securitypolicyviolation', handleCSPViolation);
            if (cspErrorDetected) {
                location.reload(); // Refresh the page if CSP violation occurred
            } else {
                displayMessage(CONFIG.SUCCESS_MESSAGE);
            }
        }, 500); // Short delay to ensure iframe load
    };

    // Create and return the download button
    const createButton = (video) => {
        const button = document.createElement('img');
        button.src = CONFIG.ICON_URL;
        button.style.cssText = `
            position: absolute; left: 10px; top: 50%; transform: translateY(-50%);
            z-index: 1000; width: 50px; height: 50px; cursor: pointer;
            border-radius: 50%; object-fit: cover; border: 2px solid rgba(255, 255, 255, 0.8);
        `;

        button.onclick = (e) => {
            e.stopPropagation(); // Prevent event bubbling
            e.preventDefault(); // Prevent default action
            const videoUrl = video.src || video.querySelector('source')?.src;
            if (videoUrl) downloadVideo(videoUrl);
            else displayMessage('Video URL Not Found');
        };
        return button;
    };

    // Handle mouseover and mouseout events to manage the video elements
    const manageVideoElements = (video) => {
        const button = createButton(video);
        video.parentNode.appendChild(button);

        const handleMouseOut = (e) => {
            if (!video.contains(e.relatedTarget) && !button.contains(e.relatedTarget)) {
                button.remove();
                video.removeEventListener('mouseout', handleMouseOut);
            }
        };

        button.addEventListener('mouseout', handleMouseOut);
        video.addEventListener('mouseout', handleMouseOut);
    };

    // Initialize the script
    const initialize = () => {
        const isPreviewPage = /v16-webapp-prime.tiktok.com|mime_type=video_mp4/.test(location.href);

        if (isPreviewPage) {
            const video = document.querySelector('video');
            if (video) {
                const link = document.createElement('a');
                link.href = video.src || video.querySelector('source')?.src;
                link.download = '';
                link.click();
            }
        } else {
            const observer = new MutationObserver(() => {
                document.querySelectorAll('video:not(.handled)').forEach((video) => {
                    video.classList.add('handled');
                    video.addEventListener('mouseover', () => manageVideoElements(video));
                });
            });

            observer.observe(document.body, { childList: true, subtree: true });
        }
    };

    initialize();
})();