// ==UserScript== // @name Tiktok Video Downloader🎬 // @namespace https://greasyfork.org/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 // @downloadURL none // ==/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); })();