// ==UserScript== // @name Threads Video Downloader // @namespace https://github.com/ManoloZocco/Threads-video-downloader-userscript // @version 1.3 // @description Download photos and videos from Threads quickly and easily! // @author P0L1T3 aka Manolo Zocco // @match https://*.threads.net/* // @connect threads.net // @connect www.threads.net // @grant GM_download // @grant GM_addStyle // @run-at document-end // @license MIT // @downloadURL none // ==/UserScript== (function() { 'use strict'; // Inserisce il CSS per il pulsante di download (da css/interface.css) GM_addStyle(` .dw { position: absolute; z-index: 5; width: 116px; height: 34px; border-radius: 8px; background: rgba(0, 0, 0, 0.6); box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3); display: none; flex-direction: row; justify-content: space-around; align-items: center; cursor: pointer; margin: 7px; border: none; color: #FFF; font-size: 11px; font-weight: 600; text-transform: uppercase; line-height: 22px; letter-spacing: -0.42px; bottom: 7px; left: 7px; } .dw .icon { background-image: url("data:image/svg+xml,%3Csvg width='20' height='20' viewBox='0 0 20 20' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M10 3v9' stroke='white' stroke-width='2' stroke-linecap='round'/%3E%3Cpath d='M6 10l4 4 4-4' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3Crect x='4' y='14' width='12' height='2' fill='white'/%3E%3C/svg%3E"); width: 20px; height: 20px; margin-right: 5px; } *:hover > .dw { display: flex; } .dw:hover { background: rgba(0, 0, 0, 0.8); } `); // Funzione di download basata su GM_download function downloadFile(url) { if (!url) return; let fileName = url.substring(url.lastIndexOf('/') + 1); if (!fileName) fileName = 'download'; GM_download({ url: url, name: fileName, onerror: function(err) { console.error('Download error:', err); } }); } // Oggetto che gestisce l'osservazione del DOM e l'iniezione dei pulsanti const downloader = { observeDom() { // Gestione dei video document.querySelectorAll("video").forEach((video) => { let container = downloader.findRoot(video); if (container && !container.querySelector(".dw")) { container.append(downloader.getBtn(video.currentSrc || video.src)); } }); // Gestione delle immagini document.querySelectorAll("img").forEach((img) => { if (img.width < 200 || img.height < 200) return; if (img.parentElement && img.parentElement.querySelector(".dw")) return; img.parentElement.prepend(downloader.getBtn(img.src)); }); }, // Crea il pulsante di download getBtn(src) { const btn = document.createElement("button"); btn.innerText = "Download"; btn.className = "dw"; const icon = document.createElement("span"); icon.className = "icon"; btn.appendChild(icon); btn.setAttribute("src", src); btn.addEventListener("click", downloader.dw); return btn; }, // Risale la gerarchia DOM per trovare un container (div con data-visualcompletion) findRoot(element) { let parent = element.parentNode; if (!parent) return null; let found = parent.querySelector("div[data-visualcompletion]"); return found || downloader.findRoot(parent); }, // Gestore dell'evento click sul pulsante dw(event) { event.preventDefault(); event.stopPropagation(); let target = event.target.nodeName.toLowerCase() === "button" ? event.target : event.target.parentElement; let src = target.getAttribute("src"); if (src) { downloadFile(src); } } }; // Avvia l'osservazione del DOM ad intervalli regolari setInterval(() => { downloader.observeDom(); }, 500); })();