您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Add download button for posts with images/videos on Threads.net
当前为
- // ==UserScript==
- // @name Threads.net Media Downloader
- // @namespace https://www.youtube.com/channel/UC26YHf9ASpeu68az2xRKn1w
- // @version 03-02-2025
- // @description Add download button for posts with images/videos on Threads.net
- // @author Kinnena
- // @match https://www.threads.net/*
- // @icon https://cdn-icons-png.flaticon.com/512/12105/12105338.png
- // @grant none
- // @license MIT
- // ==/UserScript==
- (function() {
- 'use strict';
- function addButtonToElement(element) {
- if (element.querySelector('button.my-custom-button')) return;
- const postContainer = element.closest('div[role="article"]') ||
- element.parentElement?.parentElement?.parentElement?.parentElement?.parentElement;
- if (!postContainer) return;
- const hasMedia = postContainer.querySelector('picture img, video');
- if (!hasMedia) return;
- const button = document.createElement('button');
- button.textContent = 'Download';
- button.className = 'my-custom-button';
- Object.assign(button.style, {
- position: 'relative',
- background: '#0095f6',
- color: 'white',
- border: 'none',
- borderRadius: '4px',
- padding: '6px 12px',
- margin: '4px',
- cursor: 'pointer'
- });
- button.addEventListener('click', function(event) {
- event.preventDefault();
- event.stopPropagation();
- // Get post metadata
- const spanElement = postContainer.querySelector('span[class*="x1s688f"]');
- const timeElement = postContainer.querySelector('time');
- const spanText = (spanElement?.textContent || 'unknown').replace(/[^\w]/g, '_').substring(0, 30);
- const datetime = timeElement?.getAttribute('datetime');
- // Format timestamp
- let formattedTime = '';
- if (datetime) {
- const date = new Date(datetime);
- formattedTime = [
- date.getFullYear(),
- String(date.getMonth() + 1).padStart(2, '0'),
- String(date.getDate()).padStart(2, '0'),
- '_',
- String(date.getHours()).padStart(2, '0'),
- String(date.getMinutes()).padStart(2, '0'),
- String(date.getSeconds()).padStart(2, '0')
- ].join('');
- }
- // Collect media
- const mediaElements = [
- ...postContainer.querySelectorAll('picture img'),
- ...postContainer.querySelectorAll('video')
- ];
- mediaElements.forEach((media, index) => {
- let url, type;
- if (media.tagName === 'IMG') {
- url = media.src;
- type = 'image';
- } else {
- url = media.src || media.querySelector('source')?.src;
- type = 'video';
- }
- if (url) {
- const extension = getFileExtension(url) || (type === 'image' ? 'jpg' : 'mp4');
- const filename = `Threads_${spanText}_${formattedTime}_${index + 1}.${extension}`;
- GM_download({
- url: url,
- name: filename,
- onerror: (e) => console.error('Download error:', e)
- });
- }
- });
- // Auto-like functionality
- const likeButton = postContainer.querySelector('[aria-label="讚"]');
- if (likeButton) {
- likeButton.click();
- }
- });
- element.appendChild(button);
- }
- function getFileExtension(url) {
- try {
- const cleanUrl = url.split(/[?#]/)[0];
- return cleanUrl.split('.').pop().toLowerCase();
- } catch {
- return null;
- }
- }
- function scanForButtons() {
- document.querySelectorAll('div[class*="x1fc57z9"]').forEach(addButtonToElement);
- }
- // Initial check
- scanForButtons();
- // Periodic check for new posts
- setInterval(scanForButtons, 1000);
- })();