Greasy Fork is available in English.

Fanfiction+

Improves readability and allows EBOOK DOWNLOADS!

  1. // ==UserScript==
  2. // @name Fanfiction+
  3. // @namespace DanielVestol.Fanfiction.net
  4. // @description Improves readability and allows EBOOK DOWNLOADS!
  5. // @include https://www.fanfiction.net/*
  6. // @include https://www.fictionpress.com/s/*
  7. // @version 1.20.0
  8. // @grant GM_xmlhttpRequest
  9. // ==/UserScript==
  10.  
  11. // SETTINGS
  12. const config = {
  13. ebookServer: "http://ebook.danielv.no",
  14. // ebookServer: "http://localhost",
  15. };
  16. // END OF SETTINGS
  17. console.log("DOM fully loaded and parsed");
  18.  
  19. // Better change between mobile and desktop edition.
  20. // Credits to SirCxyrtyx for snippet
  21. var mobileVersion = 'location = "https://m.fanfiction.net' + document.location.pathname + '"';
  22. // console.log(mobileVersion);
  23. document.getElementsByClassName('icon-kub-mobile')[0].setAttribute('onclick', mobileVersion);
  24.  
  25. // run code depending on what subpage we are on
  26. // for main page
  27. if (document.location.pathname == "/") {
  28. // Changing some simple styles
  29. document.getElementById('content_wrapper').style.maxWidth = '800px';
  30. // appending a style tag in HTML
  31. $('#top').append('<style>tr>td>div>ins{display:none !important;}.zmenu{display:none !important;}p{-webkit-touch-callout: text;-webkit-user-select: text;-khtml-user-select: text;-moz-user-select: text;-ms-user-select: text;user-select: text;}</style>');
  32. }
  33.  
  34. if (document.location.pathname.substr(0, 3) == "/s/") {
  35. // Changing some simple styles
  36. $('#top').append('<style>tr>td>div>ins{display:none !important;}.zmenu{display:none !important;}p{-webkit-touch-callout: text;-webkit-user-select: text;-khtml-user-select: text;-moz-user-select: text;-ms-user-select: text;user-select: text;}</style>');
  37. document.getElementById('content_wrapper').style.maxWidth = '800px';
  38. // OCD friendly review field
  39. var review = document.getElementById('review');
  40. review.getElementsByTagName('TD')[0].remove();
  41. review.getElementsByTagName('TD')[0].remove(); // Remove element 1 which is now 0
  42. review.getElementsByTagName('DIV')[0].style.maxWidth = '765px';
  43. review.getElementsByTagName('DIV')[0].style.margin = '10px';
  44. document.getElementById('review_name_value').style.maxWidth = '780px';
  45. document.getElementById('review_review').style.maxWidth = '780px';
  46. //
  47. // Show whole book function
  48. //
  49. $('#content_wrapper_inner > span:nth-child(7)').append('<button id="myShit" class="btn" type="BUTTON">Show whole book</button>');
  50. document.getElementById('myShit').addEventListener('click', function () {
  51. document.getElementById('storytextp').innerHTML = "<div id='topSuprSecret' style='position:fixed;top:100px;left:100px;width:auto;height:70px;padding-left:20px;padding-right:20px;background-color:#A2DEF2;'><h1>Loading book...</h1></div>";
  52. $.ajaxSetup({
  53. async: false
  54. });
  55. setTimeout(function () { //timeout to let it get us a progress bar ish thing
  56. for (let i = chapter; i < document.getElementById('chap_select').options.length + 1; i++) {
  57. // chapter is variable used by fanfiction, it counts what chapter you are at starting at 1.
  58. // my own call to get chapter has +1 because it fetches a list starting at 0 instead.
  59. console.log("Downloading " + document.location.pathname.substr(3, 8).replace("/", "") + " chapter " + i);
  60. var $div = $('<div>');
  61. $div.load('https://www.fanfiction.net/s/' + document.location.pathname.substr(3, 8).replace("/", "") + '/' + i + ' #storytext', function () {
  62. // now $(this)[0].innerHTML contains #storytext
  63. $("#storytextp").append($(this)[0].innerHTML);
  64. });
  65. }
  66. document.getElementById('topSuprSecret').outerHTML = '';
  67. }, 100); // end of timeout
  68. });
  69. //
  70. // EBOOKS!!!!!!!!!!!!!!!!!
  71. //
  72. $('#content_wrapper_inner > span:nth-child(7)').append('<button id="getEbook" class="btn" type="BUTTON">ebook</button>');
  73. document.getElementById('getEbook').addEventListener('click', function () {
  74. document.getElementById('storytextp').innerHTML = "<div id='topSuprSecret' style='position:fixed;top:100px;left:100px;width:auto;height:70px;padding-left:20px;padding-right:20px;background-color:#A2DEF2;'><h1>Loading book...</h1></div>";
  75. $.ajaxSetup({
  76. async: false
  77. });
  78.  
  79. getBookLegacy()
  80. // getBookWithLinks()
  81.  
  82. function getBookLegacy() {
  83. let data = {
  84. ebookMethod: "legacy",
  85. title: document.querySelector("#profile_top > b").innerHTML,
  86. author: [document.querySelector("#profile_top > a").innerHTML],
  87. description: document.querySelector("#profile_top > div").innerHTML,
  88. publisher: "fanfiction.net",
  89. contents: [],
  90. bookID: document.location.pathname.substr(3, 8).replace("/", "")
  91. };
  92. setTimeout(async function () { //timeout to let it get us a progress bar ish thing
  93. let downloaded = 0
  94. for (let i = 1; i < document.getElementById('chap_select').options.length + 1; i++) {
  95. // chapter is variable used by fanfiction, it counts what chapter you are at starting at 1.
  96. // my own call to get chapter has +1 because it fetches a list starting at 0 instead.
  97. await sleep(0.1)
  98. console.log("Downloading " + document.location.pathname.substr(3, 8).replace("/", "") + " chapter " + i);
  99. var $div = $('<div>');
  100. $div.load('https://www.fanfiction.net/s/' + document.location.pathname.substr(3, 8).replace("/", "") + '/' + i + ' #storytext', function () {
  101. // now $(this)[0].innerHTML contains #storytext
  102. data.contents.push({
  103. title: document.getElementById('chap_select').options[i - 1].innerHTML,
  104. data: $(this)[0].innerHTML,
  105. });
  106. downloaded++
  107. document.querySelector("#topSuprSecret").innerHTML = `<h1>Downloaded ${downloaded}/${document.getElementById('chap_select').options.length}</h1>`
  108. });
  109. }
  110. // Send request to server for ebook creation
  111. document.querySelector("#topSuprSecret").innerHTML = `<h1>Scrape complete, creating ebook...</h1>`
  112.  
  113. GM_xmlhttpRequest({
  114. method: "POST",
  115. url: config.ebookServer + "/epubify",
  116. data: JSON.stringify(data),
  117. headers: {
  118. "Content-Type": "application/json"
  119. },
  120. onload: function (response) {
  121. let downloadURL = config.ebookServer + "/" + document.location.pathname.substr(3, 8).replace("/", "") + ".epub"
  122. console.log(response.responseText + " book is done");
  123. if (response.responseText == "success") {
  124. // location.href = downloadURL;
  125. } else {
  126. console.log(response)
  127. alert("Could not contact ebook server. Please alert author.");
  128. }
  129. document.getElementById('topSuprSecret').outerHTML = '';
  130. document.getElementById('storytextp').innerHTML = `<div style="margin-top: 50px;">
  131. <h1>Ebook download links:</h1>
  132. <ol>
  133. <li><a href="${downloadURL}">${downloadURL}</a></li>
  134. <li><a href="${downloadURL.replace(".epub", ".epub.html")}">${downloadURL.replace(".epub", ".epub.html")}</a></li>
  135. <li><a href="${downloadURL.replace(".epub", "A4.pdf")}">${downloadURL.replace(".epub", "A4.pdf")}</a></li>
  136. <li><a href="${downloadURL.replace(".epub", "A5.pdf")}">${downloadURL.replace(".epub", "A5.pdf")}</a></li>
  137. </ol>
  138. <p>PDF versions will be ready in ~5 minutes.</p>
  139. </div>`;
  140. }
  141. });
  142.  
  143. }, 100); // end of timeout
  144. }
  145. function getBookWithLinks() {
  146. let data = {
  147. ebookMethod: "links",
  148. title: document.querySelector("#profile_top > b").innerHTML,
  149. author: [document.querySelector("#profile_top > a").innerHTML],
  150. description: document.querySelector("#profile_top > div").innerHTML,
  151. publisher: "fanfiction.net",
  152. contents: [],
  153. bookID: document.location.pathname.substr(3, 8).replace("/", "")
  154. };
  155. setTimeout(function () {
  156. if (document.getElementById('chap_select')) {
  157. for (let i = 1; i < document.getElementById('chap_select').options.length + 1; i++) {
  158. data.contents[data.contents.length] = {
  159. url: 'https://www.fanfiction.net/s/' + document.location.pathname.substr(3, 8).replace("/", "") + '/' + i,
  160. dataSelector: "#storytextp",
  161. title: document.getElementById('chap_select').options[i - 1].innerHTML,
  162. };
  163. }
  164. } else {
  165. data.contents[data.contents.length] = {
  166. url: 'https://www.fanfiction.net/s/' + document.location.pathname.substr(3, 8).replace("/", "") + '/1',
  167. dataSelector: "#storytextp",
  168. title: "Oneshot",
  169. };
  170. }
  171. document.querySelector("#topSuprSecret").innerHTML = "<h1>Converting to epub...</h1>";
  172. console.log(data);
  173. GM_xmlhttpRequest({
  174. method: "POST",
  175. url: config.ebookServer + "/epubify",
  176. data: JSON.stringify(data),
  177. headers: {
  178. "Content-Type": "application/json"
  179. },
  180. onload: function (response) {
  181. console.log(response.responseText + " book is done");
  182. if (response.responseText == "success") {
  183. // location.href = config.ebookServer + "/" + document.location.pathname.substr(3, 8).replace("/", "") + ".epub";
  184. } else {
  185. alert("Could not contact ebook server. Please alert author.");
  186. }
  187. document.getElementById('topSuprSecret').outerHTML = '';
  188. }
  189. });
  190. }, 100); // end of timeout
  191. }
  192. });
  193. }
  194. var sleep = async s => new Promise(r => setTimeout(r, s * 1000))