AO3 Length Comparator

Categorize works by length as though they were published books. Additionally, compare long works to original texts of similar length.

  1. // ==UserScript==
  2. // @name AO3 Length Comparator
  3. // @namespace http://tampermonkey.net/
  4. // @version 2024-1-25
  5. // @description Categorize works by length as though they were published books. Additionally, compare long works to original texts of similar length.
  6. // @author threeqc
  7. // @license GNU GPLv3
  8. // @match *://archiveofourown.org/*
  9. // @match *://www.archiveofourown.org/*
  10. // @icon https://www.google.com/s2/favicons?sz=64&domain=tampermonkey.net
  11. // @grant none
  12. // ==/UserScript==
  13.  
  14. // Settings
  15. // =========
  16. const show_category = true;
  17. const show_comparison = true;
  18. const category_upper_cutoff = false; // Set to false to disable upper bound (default: false)
  19. // Set to one less than comparison_lower_cutoff to show only the category or the comparison
  20.  
  21. const comparison_lower_cutoff = 150000; // Set to false to disable lower bound (default: 150000)
  22. const comparison_upper_cutoff = false; // Set to false to disable upper bound (default: false)
  23.  
  24. // Detecting drabble collections beyond a point is kind of a nightmare, it turns out. I'll probably end up changing the logic to just check for tags.
  25. const check_for_drabbles = true; // Set to false to never categorize works as drabbles. (default: true)
  26. const drabble_upper_cutoff = 500; // Set to false to allow drabble collections of arbitrary length. (default: 500)
  27. const require_100_per_chapter = false; // Set to a number to require drabbles to be 100 words per chapter beyond that word count. (default: false)
  28. const require_exact_match = true; // Set to true to require the word count to be an exact multiple of 100. The number is how far it can be from a perfect drabble. (default: true)
  29. // =========
  30.  
  31. // TODO:
  32. // 1. List all sources
  33. // 2. Be more selective about sourcing
  34. // 3. Fill gaps
  35. // 4. Clean up
  36. // 5. Establish a set of tests
  37.  
  38. // Probably don't use this list as a source for exact counts.
  39. const examples = {
  40. "5800": "The Yellow Wallpaper",
  41. "6535": "A Modest Proposal", //anycount.com
  42. // gap
  43. "7775": "The Monkey's Paw",
  44. // gap
  45. "17121": "The Little Prince", //anycount.com
  46. // gap
  47. "19965": "Common Sense",
  48. // gap
  49. "25189": "Metamorphosis", //anycount.com
  50. // gap
  51. "27000": "The Old Man and the Sea", //nytimes.com
  52. "28668": "Dr. Jekyll and Mr. Hyde", //anycount.com
  53. "29610": "Alice in Wonderland", //anycount.com
  54. "29966": "Animal Farm", //huffpost.com (publisher's weekly)
  55. "30644": "Charlie and the Chocolate Factory", //commonplacebook.com
  56. "31650": "A Christmas Carol", //anycount.com
  57. // gap
  58. "35530": "The Time Machine", //anycount.com
  59. "38000": "The Lion, The Witch, and The Wardrobe",
  60. // gap
  61. "42636": "The Wonderful Wizard of Oz", //anycount.com
  62. "43092": "Beowulf", //anycount.coms
  63. "46118": "Fahrenheit 451",
  64. "46333": "The Hitchhiker's Guide to the Galaxy", //barnesandnoble.com
  65. "47015": "A Study in Scarlet", //anycount.com
  66. "47094": "The Great Gatsby", //barnesandnoble.com
  67. "49962": "Fight Club", //barnesandnoble.com
  68. "50844": "Peter Pan", //anycount.com
  69. "52587": "A Wrinkle in Time", //wordcounters.com
  70. "53510": "We Have Always Lived in the Castle", //huffpost.com (publisher's weekly)
  71. "55000": "Annihilation [est.]",
  72. "59900": "Lord of the Flies",
  73. "60175": "The Color of Magic",
  74. "63194": "The War of the Worlds", //anycount.com
  75. "63766": "Brave New World",
  76. "65752": "The Fault in Our Stars", //wordcounters.com
  77. "66556": "The Color Purple", //commonplacebook.com
  78. "66950": "Treasure Island",
  79. "67707": "The Sun Also Rises",
  80. "70544": "The Catcher in the Rye", //wordcounters.com
  81. "72036": "Treasure Island", //anycount.com
  82. "72790": "The Other Wind", //reddit.com
  83. "78100": "Frankenstein", //anycount.com, wordcounter.tools
  84. "82222": "The Picture of Dorian Gray", //anycount.com
  85. "83705": "The Secret Garden", //anycount.com
  86. "84855": "Gilead", //commonplacebook.com
  87. "86100": "Piranesi", //thefantasyinn.com
  88. "86897": "The Scarlet Letter", //wordcounter.tools
  89. "88942": "Nineteen Eighty-Four",
  90. "89426": "The Princess Bride", //wordcounters.com *
  91. "95022": "The Hobbit", //commonplacebook.com
  92. "97364": "Anne of Green Gables",
  93. "99750": "The Hunger Games", //wordcounters.com *
  94. "100388": "To Kill A Mockingbird",
  95. "100609": "Ender's Game",
  96. "104288": "Grimm's Fairy Tails", //anycount.com
  97. "107293": "Gulliver's Travels", //anycount.com
  98. "107605": "The Adventures of Sherlock Holmes", //anycount.com
  99. "107945": "Wuthering Heights",
  100. "109185": "Good Omens",
  101. "109571": "The Adventures of Huckleberry Finn",
  102. "119394": "Sense and Sensibility",
  103. "124713": "Pride and Prejudice", //anycount.com
  104. "129443": "The Odyssey",
  105. "134710": "Schindler's List",
  106. "135420": "A Tale of Two Cities", //arbookfind.com
  107. "138138": "20000 Leagues Under the Sea", //arbookfind.com
  108. "144330": "The Da Vinci Code", //anycount.com
  109. "144523": "One Hundred Years of Solitude.", //arbookfind.com
  110. "145092": "A Tree Grows in Brooklyn", //commonplacebook.com
  111. "145469": "The Last of the Mohicans",
  112. "147317": "The Iliad",
  113. "155887": "Emma", //commonplacebook.com
  114. "156154": "Watership Down",
  115. "165453": "Dracula", //anycount.com
  116. "169481": "The Grapes of Wrath",
  117. "171479": "Oliver Twist", //fcit.usf.edu/project/cd/
  118. "174296": "Catch-22", //commonplacebook.com
  119. "187240": "Dune",
  120. "188623": "American Gods",
  121. "199016": "Great Expectations", //fcit.usf.edu/project/cd/
  122. "200000": "House of Leaves", // counts differ on this one. this is my estimate, made by taking the PDF and extracting the text, then doing my best to clean up the results, and then rounding down (by like 100) for good measure.
  123. "206052": "Moby Dick",
  124. "211591": "Crime and Punishment",
  125. "220117": "The Republic", //anycount.com
  126. "225395": "East of Eden", //commonplacebook.com
  127. "234100": "Last Argument of Kings", //reddit.com
  128. "249110": "The Hitchhiker's 'Trilogy'", //reddit.com the trilogy of all five books.
  129. "257154": "The Goblet of Fire",
  130. "265000": "Ulysses",
  131. "292727": "A Game of Thrones",
  132. "301583": "Hunger Games Trilogy",
  133. "329150": "The Earthsea Cycle",
  134. "344000": "Chronicles of Narnia",
  135. "354098": "The Brothers Karamazov", //anycount.com
  136. "360241": "David Copperfield", //anycount.com
  137. "386470": "The Way of Kings", //reddit.com
  138. "413202": "A Dance With Dragons", //reddit.com
  139. "418453": "Gone With the Wind",
  140. "421080": "Percy Jackson Series",
  141. "430269": "Don Quixote", //anycount.com
  142. "439736": "Anna Karenina",
  143. "464162": "The Count of Monte Cristo",
  144. "483994": "Infinite Jest",
  145. "530982": "Les Miserables",
  146. "561996": "Atlas Shrugged",
  147. "563675": "To Green Angel Tower",
  148. "587246": "Twilight Series",
  149. "587287": "War and Peace",
  150. "591544": "A Suitable Boy", //commonplacebook.com
  151. // gap
  152. "706574": "LoTR [w/ Silmarillion]",
  153. // gap
  154. "790678": "Bible [KJV]",
  155. "838240": "Dune Series",
  156. "874557": "Homestuck [text]", // adjusted estimates are all over the place
  157. "925100": "Witcher Series", //thefantasyinn.com
  158. "970000": "Clarissa",
  159. // gap
  160. "1084625": "Harry Potter Series",
  161. "1100000": "Sironia, Texas",
  162. "1154971": "Umineko", // https://www.reddit.com/r/umineko/comments/ccvs6n/another_estimate_of_uminekos_word_count/
  163. "1267010": "The Dark Tower Series", //thefantasyinn.com almost the exact same length
  164. "1267069": "In Search of Lost Time", //the longest commonly-distributed single book
  165. "1300000": "Zettel's Traum",
  166. // gap
  167. "1493000": "The Expanse",
  168. // gap
  169. "1736054": "A Song of Ice and Fire [1-5]",
  170. "1800000": "Mahabharata [est.]",
  171. "1901905": "The First Law", //thefantasyinn.com
  172. "2000000": "Baldur's Gate 3 [est.]", // you'd think guiness could get the precise number
  173. "2060240": "A Song of Ice and Fire [all]",
  174. "2231000": "Redwall Series", //thefantasyinn.com
  175. "2400000": "A Chronicle of Ancient Sunlight",
  176. "2473678": "Dresden Files Series", //thefantasyinn.com
  177. // Ginnungagap
  178. "3327215": "Discworld Series",
  179. "3381980": "Malazan Book of the Fallen Series", //thefantasyinn.com some of these titles are a bit unweildy
  180. "3607401": "Parahumans", //thefantasyinn.com
  181. "3786250": "In the Realms of the Unreal", //the uncontested longest published single book by one author, even if you want to count Ancient Sunlight as one book. this list is excluding stuff like The Blah Story or Marienbad My Love, which are not really stories at all.
  182. "3885275": "Realm of the Elderlings Series", //thefantasyinn.com
  183. "3991843": "Pact & Pale", // /thefantasyinn.com Pale, the second work of this arguable two-part series, was unfinished when the source was written. This is the same author (J. C. McCrae) as Parahumans, making him probably the third most prolific author in history after Pirateaba, who writes The Wandering Inn, and Jamesdean5842, who wrote The Loud House: Revamped.
  184. "4108915": "Riftwar Series",
  185. "4287886": "Wheel of Time Series",
  186. // gap
  187. "5147450": "The Sword of Truth", //thefantasyinn.com
  188. // "11970740": "The Wandering Inn", //thewanderinginn.neocities.org september 2023
  189. "13500000": "The Wandering Inn [est.]", // Naively assuming the writing has been done at a constant pace since its inception. This does include the original version of Volume 1.
  190. "44000000": "Encyclopedia Britannica [print]",
  191. // The Daoist scripture, the Daozang, might be longer, but no word count exists to my knowledge and the Encyclopedia Britannica is already longer than any fictional work.
  192. };
  193.  
  194. const factors = n => {
  195. let a = [],
  196. f = 2;
  197. while (n > 1) {
  198. if (n % f === 0) {
  199. a.push(f);
  200. n /= f;
  201. } else {
  202. f++;
  203. }
  204. }
  205. return a;
  206. };
  207.  
  208. function checkLength (length, chapter_count) {
  209. length = length.replaceAll(',', '');
  210. let examplesarr = Object.keys(examples);
  211. const closest = examples[examplesarr.reduce((a, b) => {
  212. return Math.abs(b - length) < Math.abs(a - length) ? b : a;
  213. })];
  214.  
  215. let drabble_types = ["", "Double ", "Triple ", "Quadruple ", "Quintuple ", "Sextuple ", "Septuple ", "Octuple ", "Nonuple ", "Decuple ", "Undecuple ", "Duodecuple ", "Tredecuple "];
  216. let drabble_primes = ["Double ", "Triple ", "Quintuple ", "Septuple ", "Undecuple ", "Tredecuple ", "Septendecuple ", "Novemdecuple ", "Tresvigintuple ", "Novemvigintuple ", "Untrigintuple ", "Septentrigintuple ", "Unquadragintuple ", "Tresquadragintuple ", "Septenquadragintuple ", "Tresquinquagintuple ", "Novenquinquagintuple ", "Unsexagintuple ", "Septensexgintuple ", "Unseptuagintuple ", "Treseptuagintuple ", "Novenseptuagintuple ", "Tresoctogintuple ", "Novemoctogintuple ", "Septenonagintuple "];
  217. let primes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97];
  218.  
  219. let designation = "";
  220.  
  221. if (show_category & (!category_upper_cutoff | category_upper_cutoff >= length)) {
  222. if (length == 0) {
  223. designation = "Images"
  224. } else if (check_for_drabbles & ((length <= drabble_upper_cutoff) | !drabble_upper_cutoff) & ((chapter_count == Math.round(length / 100)) | !require_100_per_chapter | require_100_per_chapter > length) & (((length % 100) <= require_exact_match) | ((length % 100) >= 100 - require_exact_match))) {
  225. let multiple = Math.round(length / 100);
  226.  
  227. if (multiple <= 13) {
  228. designation = drabble_types[multiple - 1] + "Drabble";
  229. } else {
  230. let factorized = true;
  231. let factor_list = factors(multiple);
  232.  
  233. for (let factor of factor_list) {
  234. if (primes.includes(factor)) {
  235. designation += drabble_primes[primes.indexOf(factor)];
  236. } else {
  237. designation = "Drabble Collection";
  238. factorized = false;
  239. break;
  240. }
  241. }
  242. if (factorized) {designation += "Drabble"};
  243. }
  244. } else if (length <= 500) {
  245. designation = "Microfiction";
  246. } else if (length <= 1500) {
  247. designation = "Flash Fiction";
  248. } else if (length <= 7500) {
  249. designation = "Short Story";
  250. } else if (length <= 17500) {
  251. designation = "Novelette";
  252. } else if (length <= 40000) {
  253. designation = "Novella";
  254. } else if (length <= 60000) {
  255. designation = "Short Novel";
  256. } else if (length <= 110000) {
  257. designation = "Novel";
  258. } else {
  259. designation = "Series: " + Math.max(Math.round(length/85000), 2) + "x Volumes" // different sources claim average novel length is 90000 or 80000. playing it safe.
  260. }
  261. }
  262.  
  263. if (show_comparison & (!comparison_upper_cutoff | comparison_upper_cutoff >= length) & (!comparison_lower_cutoff | length >= comparison_lower_cutoff)) {
  264. designation += ((!category_upper_cutoff | category_upper_cutoff >= length) ? ", ": "") + closest;
  265. }
  266.  
  267. return designation;
  268. }
  269.  
  270. (function() {
  271. const wces = Array.from(document.querySelectorAll('dd.words')); // get word counts
  272. var chas = [];
  273. if (check_for_drabbles) {chas = Array.from(document.querySelectorAll('dd.chapters'));} // get chapter counts
  274.  
  275. // pad the chapters list in case the word count is on a series or statistics page, in which case there will be one fewer or no chapter counts.
  276. let iters = 0;
  277. while ((chas.length != wces.length) & iters < 50) {
  278. chas.unshift(false);
  279. iters++;
  280. }
  281.  
  282. for (const [index, wc] of wces.entries()) {
  283. let chapter_data;
  284. if (chas[index].nodeName == "a") {
  285. chapter_data = chas[index].firstChild.innerText; // if there are multiple chapters
  286. } else {
  287. chapter_data = 1; // should only happen if there's only one chapter or if the element is padding
  288. }
  289. wc.innerText = wc.innerText + " (" + checkLength(wc.innerText, chapter_data) + ")";
  290. };
  291.  
  292. // code for finding gaps in the list
  293. // let oldLength = 0;
  294. // for (const [key, value] of Object.entries(examples)) {
  295. // console.log(`${Math.round((key - oldLength) / key * 100)}%`, `${oldLength} - ${key}`, value);
  296. // oldLength = key;
  297. // }
  298. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址