您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
4chan archive thread image downloader for general use across many foolfuuka based imageboards. Downloads all images individually in a thread with original filenames (by default). Optional thread API button, for development purposes.
当前为
- // ==UserScript==
- // @name 4chan Archive Image Downloader
- // @namespace Violentmonkey Scripts
- // @match https://archive.4plebs.org/*/thread/*
- // @match https://desuarchive.org/*/thread/*
- // @match https://boards.fireden.net/*/thread/*
- // @match https://boards.fireden.net/*/thread/*
- // @match https://archived.moe/*/thread/*
- // @match https://thebarchive.com/*/thread/*
- // @match https://archiveofsins.com/*/thread/*
- // @match https://www.tokyochronos.net/*/thread/*
- // @match https://archive.wakarimasen.moe/*/thread/*
- // @match https://archive.alice.al/*/thread/*
- // @grant GM_download
- // @grant GM_registerMenuCommand
- // @version 1.1
- // @license The Unlicense
- // @author ImpatientImport
- // @description 4chan archive thread image downloader for general use across many foolfuuka based imageboards. Downloads all images individually in a thread with original filenames (by default). Optional thread API button, for development purposes.
- // ==/UserScript==
- (function() {
- 'use strict';
- // Constants for later reference
- const top_of_thread = document.getElementsByClassName("post_controls")[0];
- const thread_URL = document.URL;
- const archive_site = thread_URL.toString().split('/')[2];
- const url_path = new URL(thread_URL).pathname;
- const url_path_split = url_path.toString().split('/')
- const thread_board = url_path_split[1];
- const thread_num = url_path_split[3];
- // checking URL console
- /*
- console.log(url_path_split);
- console.log(url_path);
- console.log(thread_URL);
- console.log(thread_URL.toString().split('/')[2]);
- */
- const api_url = "https://" + archive_site + "/_/api/chan/thread/?board=" + thread_board + "&num=" + thread_num; // important
- //console.log(api_url)
- /* EDIT ABOVE THIS LINE */
- // User preferences
- var indiv_button_enabled = true;
- var api_button_enabled = false;
- var keep_original_filenames = true;
- var confirm_download = true;
- /* EDIT ABOVE THIS LINE */
- // Individual thread image downloader button
- var indiv_dl_btn;
- var indiv_dlbtn_elem;
- var indivOriginalStyle;
- var indivOrigStyles;
- if (indiv_button_enabled){
- indiv_dl_btn = document.createElement('a');
- indiv_dl_btn.id = "indiv_btn";
- indiv_dl_btn.classList.add("btnr", "parent");
- indiv_dl_btn.innerText = "Indiv DL";
- top_of_thread.append(indiv_dl_btn);
- indiv_dlbtn_elem = document.getElementById("indiv_btn");
- indivOriginalStyle = window.getComputedStyle(indiv_dl_btn);
- indivOrigStyles = {
- backgroundColor: indivOriginalStyle.backgroundColor,
- color: indivOriginalStyle.color,
- }
- }
- // API button for getting the JSON of a thread in a new tab
- var api_btn;
- var api_btn_elem;
- if (api_button_enabled){
- api_btn = document.createElement('a');
- api_btn.id = "api_btn";
- api_btn.href = api_url;
- api_btn.target = "new";
- api_btn.classList.add("btnr", "parent");
- api_btn.innerText = "Thread API";
- top_of_thread.append(api_btn);
- api_btn_elem = document.getElementById("api_btn");
- }
- function displayButton (elem){
- console.log(elem);
- var current_style = window.getComputedStyle(elem).backgroundColor;
- //console.log(current_style); // debug
- var next_style;
- const button_original_text = {"indiv_btn": "Indiv DL"};
- const button_original_styles = {"indiv_btn": indivOrigStyles};
- const confirmStyles = {
- backgroundColor: 'rgb(255, 64, 64)', // Coral Red
- color:"white",
- }
- const processingStyles = {
- backgroundColor: 'rgb(238, 210, 2)', // Safety Yellow
- color:"black",
- }
- const doneStyles = {
- backgroundColor: 'rgb(46, 139, 87)', // Sea Green
- color:"white",
- }
- const originalStyles = {
- backgroundColor: button_original_styles[elem.id].backgroundColor, // Original, clear
- color: button_original_styles[elem.id].color,
- }
- // Button style switcher
- switch (current_style) {
- case 'rgba(0, 0, 0, 0)': // Original color
- next_style = confirmStyles;
- elem.innerText = "Confirm?";
- break;
- case 'rgb(255, 64, 64)': // Confirm color
- next_style = processingStyles;
- elem.innerText = "Processing";
- break;
- case 'rgb(238, 210, 2)': // Processing color
- next_style = doneStyles;
- elem.innerText = "Done";
- break;
- case 'rgb(46, 139, 87)': // Done Color
- next_style = originalStyles;
- elem.innerText = button_original_text[elem.id];
- break;
- }
- Object.assign(elem.style, next_style);
- }
- // Retrieves media from the thread (in JSON format)
- // If OP only, ignore posts, else get posts
- function retrieve_media(thread_obj) {
- var media_arr = [];
- var media_fnames = [];
- var return_value = [];
- const OP = thread_obj[thread_num].op.media;
- //console.log(OP); // debug
- media_arr.push(OP.media_link);
- media_fnames.push(OP.media_filename);
- // Boolean, checks if posts are present in thread
- const posts_exist = thread_obj[thread_num].posts != undefined;
- if (posts_exist) {
- const thread_posts = thread_obj[thread_num].posts;
- const post_nums = Object.keys(thread_posts);
- const posts_length = post_nums.length;
- //Adds all post image urls and original filenames to the above arrays
- for (let i = 0; i < posts_length; i++) {
- //equivalent to: thread[posts][post_num][media]
- var temp_media_post = thread_posts[post_nums[i]].media;
- //if media exists,
- if (temp_media_post !== null) {
- //then push media to arrays
- media_arr.push(temp_media_post.media_link)
- if (keep_original_filenames){
- media_fnames.push(temp_media_post.media_filename);
- //console.log(temp_media_post.media_filename); //debug
- }
- else{
- media_fnames.push(temp_media_post.media_orig);
- //console.log(temp_media_post.media_orig); //debug
- }
- }
- }
- }
- // Adds the media link array with the media filenames array into the final return
- return_value[0] = media_arr;
- return_value[1] = media_fnames;
- for (var i=0; i<media_arr.length; i++){
- //console.log(media_fnames[i] + " "+ media_arr[i]); //debug
- GM_download(media_arr[i], media_fnames[i]);
- }
- displayButton(indiv_dlbtn_elem);
- setTimeout(displayButton(indiv_dlbtn_elem), 3000);
- }
- // Gets the JSON file for the 4plebs thread with the API
- async function get_archive_thread() {
- const API_response = await fetch(api_url);
- const JSON_file = await API_response.json();
- console.log(JSON_file); // debug
- retrieve_media(JSON_file);
- }
- // Controls what the individual download button does upon being clicked
- function indivDownload(){
- displayButton(indiv_dlbtn_elem);
- // Wait for user to confirm zip if didn't click fast enough for double-click
- setTimeout(function(){
- if (window.getComputedStyle(indiv_dl_btn).backgroundColor == 'rgb(255, 64, 64)'){
- indiv_dl_btn.removeEventListener("click", displayButton);
- indiv_dl_btn.addEventListener("click", get_archive_thread);
- // If user does not confirm, reset the button back to original
- setTimeout(function(){
- indiv_dl_btn.removeEventListener("click", get_archive_thread);
- indiv_dl_btn.addEventListener("click", displayButton);
- Object.assign(indiv_dlbtn_elem.style, indivOrigStyles);
- indiv_dl_btn.innerText = "Indiv DL";
- }, 5000);
- }
- }, 501);
- }
- GM_registerMenuCommand("Download all thread images individually", get_archive_thread);
- // Download thread button event listener(s)
- if(confirm_download){
- indiv_dlbtn_elem.addEventListener("click", indivDownload);
- indiv_dlbtn_elem.addEventListener("dblclick", get_archive_thread);
- }
- else{
- indiv_dlbtn_elem.addEventListener("click", get_archive_thread);
- }
- })();