Frenschan Thread Image Downloader

Downloads all images individually in a frenschan thread with original filenames (by default).

  1. // ==UserScript==
  2. // @name Frenschan Thread Image Downloader
  3. // @namespace Violentmonkey Scripts
  4. // @match https://*.frenschan.com/*/res/*.html
  5. // @match https://*.frenschan.org/*/res/*.html
  6. // @grant GM_download
  7. // @grant GM_registerMenuCommand
  8. // @version 1.2.1
  9. // @license The Unlicense
  10. // @author ImpatientImport
  11. // @description Downloads all images individually in a frenschan thread with original filenames (by default).
  12. // ==/UserScript==
  13.  
  14.  
  15. /* EDIT BELOW THIS LINE */
  16.  
  17. // User preferences
  18.  
  19. var download_limit = 3000; // speed in milliseconds to delay
  20.  
  21. /* EDIT ABOVE THIS LINE */
  22.  
  23.  
  24. function sleep(ms) {
  25. return new Promise(resolve => setTimeout(resolve, ms));
  26. }
  27.  
  28. (function() {
  29. 'use strict';
  30. // Constants for later reference
  31. const top_of_thread = document.getElementsByClassName("intro")[0];
  32. const thread_URL = document.URL;
  33. const tinyboard_site = thread_URL.toString().split('/')[2];
  34. const url_path = new URL(thread_URL).pathname;
  35. const url_path_split = url_path.toString().split('/')
  36. const thread_board = url_path_split[1];
  37. const thread_num = url_path_split[3].split(".")[0];
  38. const json_url = "https://"+ tinyboard_site + "/"+ thread_board +"/res/"+ thread_num + ".json"; // important
  39. var media_arr=[], media_fnames=[];
  40. // Gets the JSON file for the Tinyboard thread
  41. async function get_archive_thread() {
  42. const site_response = await fetch(json_url);
  43. const JSON_file = await site_response.json();
  44. console.log(JSON_file); // debug
  45. retrieve_media(JSON_file);
  46. download_images();
  47. }
  48. // Retrieves media from the thread (in JSON format)
  49. function retrieve_media(thread_obj) {
  50. const posts_exist = thread_obj.posts != undefined;
  51.  
  52. if (posts_exist) {
  53. const thread_posts = thread_obj.posts;
  54. //const post_nums = Object.keys(thread_posts);
  55. const posts_length = thread_posts.length;
  56. for (let i = 0; i < posts_length; i++) {
  57. if(thread_posts[i].tim != null && thread_posts[i].ext != "deleted"){
  58. media_arr.push("https://" + tinyboard_site + "/" + thread_board + "/src/" + thread_posts[i].tim + thread_posts[i].ext );
  59. media_fnames.push(thread_posts[i].filename + thread_posts[i].ext);
  60. //in case of more than one image in post
  61. var extra = thread_posts[i].extra_files
  62. if(extra != null){
  63. for (let j=0; j < extra.length; j++){
  64. media_arr.push("https://" + tinyboard_site + "/" + thread_board + "/src/" + extra[j].tim + extra[j].ext );
  65. media_fnames.push(extra[j].filename + extra[j].ext);
  66. }
  67. }
  68. }
  69. }
  70. }
  71. }
  72. async function download_images(){
  73.  
  74. for (var i=0; i<media_arr.length; i++){
  75.  
  76. await sleep(download_limit);
  77. console.log(media_arr[i]);
  78.  
  79. GM_download(media_arr[i], media_fnames[i]) // downloads images
  80. }
  81. }
  82. GM_registerMenuCommand("Download all thread images individually", get_archive_thread);
  83. var indiv_dl_btn;
  84. var indiv_dlbtn_elem;
  85. indiv_dl_btn = document.createElement('a');
  86. indiv_dl_btn.id = "indiv_btn";
  87. indiv_dl_btn.innerText = "[Download Images]";
  88. top_of_thread.append(indiv_dl_btn);
  89. indiv_dlbtn_elem = document.getElementById("indiv_btn");
  90. indiv_dl_btn.addEventListener("click", get_archive_thread);
  91. })();