Add a download button to a specific div on Threads.net to download all images in the <picture> tag of the post.
// ==UserScript==
// @name Threads.net Image Downloader
// @namespace http://tampermonkey.net/
// @version 1.6
// @license MIT
// @esversion 11
// @description Add a download button to a specific div on Threads.net to download all images in the <picture> tag of the post.
// @author StevenJon0826
// @match https://www.threads.net/*
// @grant GM_download
// ==/UserScript==
// ==UserScript==
// @name Threads.net Image Downloader (Button in Specified Div)
// @namespace http://tampermonkey.net/
// @version 1.6
// @license MIT
// @esversion 11
// @description Add a download button to a specific div on Threads.net to download all images in the <picture> tag of the post.
// @author StevenJon0826
// @match https://www.threads.net/*
// @grant GM_download
// ==/UserScript==
(function() {
'use strict';
function addButtonToElement(element) {
// 檢查該元素是否已經存在按鈕,避免重複加入
if (!element.querySelector('button.my-custom-button')) {
// 建立按鈕
const button = document.createElement('button');
button.textContent = 'Download';
button.classList.add('my-custom-button');
button.style.position = 'relative';
// 當按鈕被點擊時,往上找兩層並統計<picture>元素內的<img>數量
button.addEventListener('click', function() {
// 阻止事件的預設行為和冒泡
event.preventDefault();
event.stopPropagation();
// 往上找兩層 //x1s688f
const grandparentElement = element.parentElement?.parentElement?.parentElement?.parentElement?.parentElement;
if (grandparentElement) {
// 在祖先層級中尋找所有的<picture>元素
const pictures = grandparentElement.querySelectorAll('picture img');
// 找到 class 包含 x1s688f 的 <span> 並取得其文字內容
const spanElement = grandparentElement.querySelector('span[class*="x1s688f"]');
let spanText;
if (spanElement) {
spanText = spanElement.textContent; // 取得文字內容
}
const timeElement = grandparentElement.querySelector('time'); // 假設只有一個time元素
let formattedTime;
if (timeElement) {
const datetimeValue = timeElement.getAttribute('datetime'); // 取得datetime屬性
const dateObject = new Date(datetimeValue); // 將datetime轉換為Date物件
// 格式化日期為YYYYMMDD_hhmmss
const year = dateObject.getFullYear();
const month = String(dateObject.getMonth() + 1).padStart(2, '0'); // 月份從0開始,所以加1
const day = String(dateObject.getDate()).padStart(2, '0');
const hours = String(dateObject.getHours()).padStart(2, '0');
const minutes = String(dateObject.getMinutes()).padStart(2, '0');
const seconds = String(dateObject.getSeconds()).padStart(2, '0');
formattedTime = `${year}${month}${day}_${hours}${minutes}${seconds}`;
}
pictures.forEach((img, index) => {
const imageUrl = img.src;
const filename = `Threads-${spanText}-${formattedTime}-${index + 1}.jpg`;
GM_download(imageUrl, filename);
});
// 找到 aria-label="讚" 的元素並模擬點擊
const likeButton = grandparentElement.querySelector('[aria-label="讚"]');
if (likeButton) {
if (typeof likeButton.click === 'function') {
likeButton.click(); // 如果元素有 click 方法,模擬點擊
} else {
// 如果該元素是SVG,則創建一個事件手動觸發
const event = new MouseEvent('click', {
bubbles: true,
cancelable: true
});
likeButton.dispatchEvent(event); // 模擬點擊事件
}
console.log('Like button clicked');
} else {
console.log('No like button with aria-label "讚" found');
}
} else {
console.log('Could not find grandparent element');
}
});
// 將按鈕加入到該元素中
element.appendChild(button);
}
}
function scanForElements() {
// 定期掃描符合條件的元素
const elements = document.querySelectorAll('div[class*="x1fc57z9"]');
elements.forEach(addButtonToElement);
}
// 使用setInterval每隔一段時間檢查畫面上是否有符合條件的元素
setInterval(scanForElements, 500); // 每秒檢查一次
})();