Hitomi - Search & UI Tweaks

Various search filters and user experience enhancers

  1. // ==UserScript==
  2. // @name Hitomi - Search & UI Tweaks
  3. // @namespace brazenvoid
  4. // @version 6.1.6
  5. // @author brazenvoid
  6. // @license GPL-3.0-only
  7. // @description Various search filters and user experience enhancers
  8. // @match https://hitomi.la/*
  9. // @require https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js
  10. // @require https://update.greasyfork.org/scripts/375557/1244990/Base%20Brazen%20Resource.js
  11. // @require https://update.greasyfork.org/scripts/416104/1451214/Brazen%20UI%20Generator.js
  12. // @require https://update.greasyfork.org/scripts/418665/1408619/Brazen%20Configuration%20Manager.js
  13. // @require https://update.greasyfork.org/scripts/429587/1244644/Brazen%20Item%20Attributes%20Resolver.js
  14. // @require https://update.greasyfork.org/scripts/416105/1384192/Brazen%20Base%20Search%20Enhancer.js
  15. // @grant GM_addStyle
  16. // @run-at document-end
  17. // ==/UserScript==
  18.  
  19. GM_addStyle(
  20. `#settings-wrapper{min-width:400px;width:400px}.disliked-tag{background-color:lightcoral !important}.disliked-tag:hover{background-color:indianred !important}.disliked-tag.favourite-tag{background-color:orange !important}.disliked-tag.favourite-tag:hover{background-color:darkorange !important}.favourite-tag{background-color:mediumseagreen !important}.favourite-tag:hover{background-color:forestgreen !important}`)
  21.  
  22. const IS_GALLERY_PAGE = $('#dl-button').length
  23.  
  24. const FILTER_GALLERY_TYPES = 'Show Gallery Types'
  25. const FILTER_PAGES = 'Pages'
  26. const FILTER_LANGUAGES = 'Languages'
  27.  
  28. const OPTION_REMOVE_RELATED_GALLERIES = 'Remove Related Galleries'
  29.  
  30. const UI_FAVOURITE_TAGS = 'Favourite Tags'
  31. const UI_DISLIKED_TAGS = 'Disliked Tags'
  32. const UI_SHOW_ALL_TAGS = 'Show All Gallery Tags'
  33.  
  34. const ITEM_GALLERY_TYPE = 'Gallery Type'
  35. const ITEM_LANGUAGE = 'Language'
  36.  
  37. class HitomiSearchAndUITweaks extends BrazenBaseSearchEnhancer
  38. {
  39. constructor()
  40. {
  41. super({
  42. isUserLoggedIn: false,
  43. itemDeepAnalysisSelector: '',
  44. itemLinkSelector: '',
  45. itemListSelectors: '.gallery-content',
  46. itemNameSelector: 'h1.lillie a',
  47. itemSelectors: '.anime,.acg,.dj,.cg,.imageset,.manga',
  48. requestDelay: 0,
  49. scriptPrefix: 'hitomi-sui-',
  50. tagSelectorGenerator: (tag) => {
  51. let selector
  52. tag = encodeURIComponent(tag.trim()).replace('-', '%2D')
  53.  
  54. if (tag.startsWith('artist%3A')) {
  55. selector = 'a[href="/artist/' + tag.replace('artist%3A', '') + '-all.html"]'
  56. } else
  57. if (tag.startsWith('series%3A')) {
  58. selector = 'a[href="/series/' + tag.replace('series%3A', '') + '-all.html"]'
  59. } else {
  60. selector = 'a[href="/tag/' + tag + '-all.html"]'
  61. }
  62. return selector
  63. },
  64. })
  65. this._setupFeatures()
  66. this._setupUI()
  67. this._setupEvents()
  68. }
  69.  
  70. /**
  71. * @private
  72. */
  73. _setupEvents()
  74. {
  75. if (IS_GALLERY_PAGE) {
  76.  
  77. this._onUIBuild(() => this._performComplexOperation(
  78. FILTER_PAGES,
  79. (range) => !this._getConfig(OPTION_DISABLE_COMPLIANCE_VALIDATION) && this._configurationManager.generateValidationCallback(FILTER_PAGES)(range),
  80. (range) => {
  81. let navPages = $('.simplePagerNav li').length
  82. let pageCount = navPages > 0 ? navPages * 50 : $('.simplePagerPage1').length
  83. if (!Validator.isInRange(pageCount, range.minimum, range.maximum)) {
  84. top.close()
  85. }
  86. }),
  87. )
  88.  
  89. this._onUIBuild(
  90. () => this._performOperation(OPTION_REMOVE_RELATED_GALLERIES, () => $('.gallery-content').remove()))
  91. }
  92.  
  93. this._onUIBuilt(() => this._uiGen.getSelectedSection()[0].userScript = this)
  94.  
  95. this._onFirstHitAfterCompliance.push((item) => {
  96. this._performComplexOperation(UI_SHOW_ALL_TAGS, (flag) => flag && !IS_GALLERY_PAGE, () => {
  97. let tags = item.find('.relatedtags > ul > li')
  98. let lastTag = tags.last()
  99. if (lastTag.text() === '...') {
  100. lastTag.remove()
  101. tags.filter('.hidden-list-item').removeClass('hidden-list-item')
  102. }
  103. })
  104. })
  105. }
  106.  
  107. /**
  108. * @private
  109. */
  110. _setupFeatures()
  111. {
  112. this._configurationManager
  113. .addCheckboxesGroup(FILTER_GALLERY_TYPES, [
  114. ['Anime', 'anime'],
  115. ['Artist CG', 'acg'],
  116. ['Doujinshi', 'dj'],
  117. ['Game CG', 'cg'],
  118. ['Image Set', 'imageset'],
  119. ['Manga', 'manga'],
  120. ], 'Show only selected gallery types.')
  121. .addCheckboxesGroup(FILTER_LANGUAGES, [
  122. ['N/A', 'not-applicable'],
  123. ['Japanese', 'japanese'],
  124. ['Chinese', 'chinese'],
  125. ['English', 'english'],
  126. ['Albanian', 'albanian'],
  127. ['Arabic', 'arabic'],
  128. ['Bulgarian', 'bulgarian'],
  129. ['Catalan', 'catalan'],
  130. ['Cebuano', 'cebuano'],
  131. ['Czech', 'czech'],
  132. ['Danish', 'danish'],
  133. ['Dutch', 'dutch'],
  134. ['Esperanto', 'esperanto'],
  135. ['Estonian', 'estonian'],
  136. ['Finnish', 'finnish'],
  137. ['French', 'french'],
  138. ['German', 'german'],
  139. ['Greek', 'greek'],
  140. ['Hebrew', 'hebrew'],
  141. ['Hungarian', 'hungarian'],
  142. ['Indonesian', 'indonesian'],
  143. ['Italian', 'italian'],
  144. ['Korean', 'korean'],
  145. ['Latin', 'latin'],
  146. ['Mongolian', 'mongolian'],
  147. ['Norwegian', 'norwegian'],
  148. ['Persian', 'persian'],
  149. ['Polish', 'polish'],
  150. ['Portuguese', 'portuguese'],
  151. ['Romanian', 'romanian'],
  152. ['Russian', 'russian'],
  153. ['Slovak', 'slovak'],
  154. ['Spanish', 'spanish'],
  155. ['Swedish', 'swedish'],
  156. ['Tagalog', 'tagalog'],
  157. ['Thai', 'thai'],
  158. ['Turkish', 'turkish'],
  159. ['Ukrainian', 'ukrainian'],
  160. ['Unspecified', 'unspecified'],
  161. ['Vietnamese', 'vietnamese'],
  162. ], 'Select languages to show')
  163. .addFlagField(OPTION_REMOVE_RELATED_GALLERIES, 'Remove related galleries section from gallery pages.')
  164. .addFlagField(UI_SHOW_ALL_TAGS, 'Show all gallery tags in search results.')
  165. .addRangeField(FILTER_PAGES, 0, Infinity, 'Close gallery pages that don\'t satisfy these page limits. Only works on galleries opened in new tabs.')
  166.  
  167. this._itemAttributesResolver
  168. .addAttribute(ITEM_GALLERY_TYPE, (item) => item.attr('class'))
  169. .addAttribute(ITEM_LANGUAGE, (item) => {
  170. let link = item.find('tr:nth-child(3) > td:nth-child(2) a')
  171. if (link.length) {
  172. return link.attr('href').replace('/index-', '').replace('.html', '')
  173. }
  174. return 'not-applicable'
  175. })
  176.  
  177. this._addItemComplianceFilter(FILTER_LANGUAGES, ITEM_LANGUAGE)
  178. this._addItemComplianceFilter(FILTER_GALLERY_TYPES, ITEM_GALLERY_TYPE)
  179.  
  180. let otherTagSections = IS_GALLERY_PAGE ? $('.tags') : null
  181.  
  182. this._addItemTagHighlights(UI_FAVOURITE_TAGS, otherTagSections, 'favourite-tag', 'Specify favourite tags to highlight. "&" "|" can be used.')
  183. this._addItemTagHighlights(UI_DISLIKED_TAGS, otherTagSections, 'disliked-tag', 'Specify disliked tags to highlight. "&" "|" can be used.')
  184. this._addItemTagBlacklistFilter(8)
  185. }
  186.  
  187. /**
  188. * @private
  189. */
  190. _setupUI()
  191. {
  192. this._userInterface = [
  193. this._uiGen.createTabsSection(['Filters', 'Highlights', 'Languages', 'Global', 'Stats'], [
  194. this._uiGen.createTabPanel('Filters', true).append([
  195. this._configurationManager.createElement(FILTER_GALLERY_TYPES),
  196. this._uiGen.createSeparator(),
  197. this._configurationManager.createElement(FILTER_PAGES),
  198. this._uiGen.createSeparator(),
  199. this._configurationManager.createElement(OPTION_ENABLE_TAG_BLACKLIST),
  200. this._configurationManager.createElement(FILTER_TAG_BLACKLIST),
  201. ]),
  202. this._uiGen.createTabPanel('Highlights').append([
  203. this._configurationManager.createElement(UI_FAVOURITE_TAGS),
  204. this._configurationManager.createElement(UI_DISLIKED_TAGS),
  205. ]),
  206. this._uiGen.createTabPanel('Languages').append([
  207. this._configurationManager.createElement(FILTER_LANGUAGES),
  208. ]),
  209. this._uiGen.createTabPanel('Global').append([
  210. this._configurationManager.createElement(UI_SHOW_ALL_TAGS),
  211. this._configurationManager.createElement(OPTION_ALWAYS_SHOW_SETTINGS_PANE),
  212. this._uiGen.createSeparator(),
  213. this._createSettingsBackupRestoreFormActions(),
  214. ]),
  215. this._uiGen.createTabPanel('Stats').append([
  216. this._uiGen.createStatisticsFormGroup(FILTER_GALLERY_TYPES),
  217. this._uiGen.createStatisticsFormGroup(FILTER_LANGUAGES),
  218. this._uiGen.createStatisticsFormGroup(FILTER_TAG_BLACKLIST),
  219. this._uiGen.createSeparator(),
  220. this._uiGen.createStatisticsTotalsGroup(),
  221. ]),
  222. ]),
  223. this._configurationManager.createElement(OPTION_DISABLE_COMPLIANCE_VALIDATION),
  224. this._uiGen.createSeparator(),
  225. this._createSettingsFormActions(),
  226. this._uiGen.createSeparator(),
  227. this._uiGen.createStatusSection(),
  228. ]
  229. }
  230. }
  231.  
  232. (new HitomiSearchAndUITweaks).init()