Hitomi - Search & UI Tweaks

Various search filters and user experience enhancers

La data de 26-12-2020. Vezi ultima versiune.

  1. // ==UserScript==
  2. // @name Hitomi - Search & UI Tweaks
  3. // @namespace brazenvoid
  4. // @version 3.0.0
  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.5.1/jquery.min.js
  10. // @require https://greasyfork.org/scripts/375557-base-resource/code/Base%20Resource.js?version=884246
  11. // @require https://greasyfork.org/scripts/418665-brazen-configuration-manager/code/Brazen%20Configuration%20Manager.js?version=880818
  12. // @require https://greasyfork.org/scripts/416104-brazen-ui-generator/code/Brazen%20UI%20Generator.js?version=880816
  13. // @require https://greasyfork.org/scripts/416105-brazen-base-search-enhancer/code/Brazen%20Base%20Search%20Enhancer.js?version=884245
  14. // @grant GM_addStyle
  15. // @run-at document-end
  16. // ==/UserScript==
  17.  
  18. GM_addStyle(
  19. `#settings-wrapper{font-family:Open Sans;font-size:12px;font-weight:700;line-height:1;background-color:rgb(216, 210, 234);top:5vh;width:220px}button{font-family:Open Sans;font-size:12px;font-weight:700}.bg-brand{background-color:rgb(216, 210, 234)}`)
  20.  
  21. const PAGE_PATH_NAME = window.location.pathname
  22.  
  23. const IS_GALLERY_PAGE = $('#dl-button').length
  24.  
  25. const ITEM_CLASSES = ['acg', 'anime', 'cg', 'dj', 'manga']
  26. const ITEM_CLASSES_SELECTOR = '.acg,.anime,.cg,.dj,.manga'
  27.  
  28. const FILTER_GALLERY_TYPES = 'Show Gallery Types'
  29. const FILTER_TAG_BLACKLIST = 'Tag Blacklist'
  30. const FILTER_COMPLEX_TAG_BLACKLIST = 'Complex Blacklist'
  31. const FILTER_LANGUAGES = 'Languages'
  32.  
  33. const OPTION_REMOVE_RELATED_GALLERIES = 'Remove Related Galleries'
  34.  
  35. class HitomiSearchAndUITweaks extends BrazenBaseSearchEnhancer
  36. {
  37. constructor ()
  38. {
  39. super('hitomi-sui-', ITEM_CLASSES)
  40.  
  41. this._configurationManager.
  42. addCheckboxesGroup(FILTER_GALLERY_TYPES, [
  43. ['Anime', 'anime'],
  44. ['Artist CG', 'acg'],
  45. ['Doujinshi', 'dj'],
  46. ['Game CG', 'cg'],
  47. ['Manga', 'manga'],
  48. ], 'Show only selected gallery types.').
  49. addCheckboxesGroup(FILTER_LANGUAGES, [
  50. ['N/A', 'not-applicable'],
  51. ['Japanese', 'japanese'],
  52. ['Chinese', 'chinese'],
  53. ['English', 'english'],
  54. ['Albanian', 'albanian'],
  55. ['Arabic', 'arabic'],
  56. ['Bulgarian', 'bulgarian'],
  57. ['Catalan', 'catalan'],
  58. ['Cebuano', 'cebuano'],
  59. ['Czech', 'czech'],
  60. ['Danish', 'danish'],
  61. ['Dutch', 'dutch'],
  62. ['Esperanto', 'esperanto'],
  63. ['Estonian', 'estonian'],
  64. ['Finnish', 'finnish'],
  65. ['French', 'french'],
  66. ['German', 'german'],
  67. ['Greek', 'greek'],
  68. ['Hebrew', 'hebrew'],
  69. ['Hungarian', 'hungarian'],
  70. ['Indonesian', 'indonesian'],
  71. ['Italian', 'italian'],
  72. ['Korean', 'korean'],
  73. ['Latin', 'latin'],
  74. ['Mongolian', 'mongolian'],
  75. ['Norwegian', 'norwegian'],
  76. ['Persian', 'persian'],
  77. ['Polish', 'polish'],
  78. ['Portuguese', 'portuguese'],
  79. ['Romanian', 'romanian'],
  80. ['Russian', 'russian'],
  81. ['Slovak', 'slovak'],
  82. ['Spanish', 'spanish'],
  83. ['Swedish', 'swedish'],
  84. ['Tagalog', 'tagalog'],
  85. ['Thai', 'thai'],
  86. ['Turkish', 'turkish'],
  87. ['Ukrainian', 'ukrainian'],
  88. ['Unspecified', 'unspecified'],
  89. ['Vietnamese', 'vietnamese'],
  90. ], 'Select languages to show').
  91. addFlagField(OPTION_REMOVE_RELATED_GALLERIES, 'Remove related galleries section from gallery pages.').
  92. addRulesetField(
  93. FILTER_TAG_BLACKLIST,
  94. 'Specify the tags blacklist with one tag on each line.',
  95. (values) => {
  96. for (let i = 0; i < values.length; i++) {
  97. values[i] = decodeURIComponent(values[i])
  98. }
  99. return values
  100. },
  101. (values) => {
  102. for (let i = 0; i < values.length; i++) {
  103. values[i] = encodeURIComponent(values[i])
  104. }
  105. return values
  106. },
  107. ).
  108. addRulesetField(FILTER_COMPLEX_TAG_BLACKLIST, 'Set complex tag blacklist rules on each line.')
  109.  
  110. this._setupUI()
  111. this._setupCompliance()
  112. this._setupComplianceFilters()
  113. }
  114.  
  115. /**
  116. * @param {string} str
  117. * @return {string}
  118. * @private
  119. */
  120. _encodeURIComponentRFC3986 (str)
  121. {
  122. return encodeURIComponent(str).replace(/[-!'()*]/g, (c) => '%' + c.charCodeAt(0).toString(16).toUpperCase())
  123. }
  124.  
  125. _formatFilter (prefix, filter, suffix)
  126. {
  127. return '[href="' + prefix + this._encodeURIComponentRFC3986(filter) + suffix + '.html"]'
  128. }
  129.  
  130. /**
  131. * @param filters
  132. * @param prefix
  133. * @param suffix
  134. * @param join
  135. * @return {*}
  136. * @private
  137. */
  138. _formatFilters (filters, prefix, suffix, join)
  139. {
  140. let formattedFilters = [], index2
  141. for (let index = 0; index < filters.length; index++) {
  142. if (Array.isArray(filters[index])) {
  143. for (index2 = 0; index2 < filters[index].length; index2++) {
  144. formattedFilters[index][index2] = this._formatFilter(prefix, filters[index][index2], suffix)
  145. }
  146. } else {
  147. formattedFilters[index] = this._formatFilter(prefix, filters[index], suffix)
  148. }
  149. }
  150. return join ? formattedFilters.join(', ') : formattedFilters
  151. }
  152.  
  153. /**
  154. * @private
  155. */
  156. _removeRelatedGalleries ()
  157. {
  158. if (IS_GALLERY_PAGE && this._configurationManager.getValue(OPTION_REMOVE_RELATED_GALLERIES)) {
  159. $('.gallery-content').remove()
  160. }
  161. }
  162.  
  163. _setupCompliance ()
  164. {
  165. this._onGetItemLists = () => $('.gallery-content')
  166.  
  167. this._onGetItemName = (item) => item.find('h1.lillie a').text()
  168. }
  169.  
  170. _setupComplianceFilters ()
  171. {
  172. this._addItemComplianceFilter(FILTER_LANGUAGES, (valueKeys) => valueKeys.length, (item, valueKeys) => {
  173. let languageLink = item.find('tr:nth-child(3) > td:nth-child(2) a')
  174. if (languageLink.length) {
  175.  
  176. languageLink = languageLink.attr('href')
  177. for (let key of valueKeys) {
  178.  
  179. if (languageLink.includes(key)) {
  180. return true
  181. }
  182. }
  183. return false
  184. }
  185. return valueKeys.includes('not-applicable')
  186. })
  187. this._addItemComplianceFilter(FILTER_GALLERY_TYPES, (valueKeys) => valueKeys.length, (item, valueKeys) => {
  188. for (let galleryClass of valueKeys) {
  189. if (item.hasClass(galleryClass)) {
  190. return true
  191. }
  192. }
  193. return false
  194. })
  195. this._addItemComplianceFilter(FILTER_TAG_BLACKLIST, (blacklistedTags) => blacklistedTags.length, (item, blacklistedTags) => {
  196. let relatedTagsWrapper = item.find('.relatedtags')
  197. for (let blacklistedTag of blacklistedTags) {
  198. if (relatedTagsWrapper.find('a[href*="' + blacklistedTag + '"]').length) {
  199. return false
  200. }
  201. }
  202. return true
  203. })
  204. this._addItemComplianceFilter(
  205. FILTER_COMPLEX_TAG_BLACKLIST, (complexBlacklist) => complexBlacklist.length, (item, complexBlacklist) => {
  206. for (let tagGroupFilterSelectors of complexBlacklist) {
  207. if (!item.find(tagGroupFilterSelectors.join(', ')).length < tagGroupFilterSelectors.length) {
  208. return false
  209. }
  210. }
  211. return true
  212. })
  213. }
  214.  
  215. _setupUI ()
  216. {
  217. this._onBeforeUIBuild = () => {
  218. if (IS_GALLERY_PAGE) {
  219. this._removeRelatedGalleries()
  220. }
  221. }
  222.  
  223. this._onUIBuild = () =>
  224. this._uiGen.createSettingsSection().append([
  225. this._uiGen.createTabsSection(['Filters', 'Languages', 'Statistics'], [
  226. this._uiGen.createTabPanel('Filters', true).append([
  227. this._configurationManager.createElement(FILTER_GALLERY_TYPES),
  228. this._uiGen.createSeparator(),
  229. this._configurationManager.createElement(FILTER_TAG_BLACKLIST),
  230. // this._configurationManager.createElement(FILTER_COMPLEX_TAG_BLACKLIST),
  231. this._uiGen.createSeparator(),
  232. this._configurationManager.createElement(OPTION_DISABLE_COMPLIANCE_VALIDATION),
  233. this._uiGen.createSeparator(),
  234. this._configurationManager.createElement(OPTION_ALWAYS_SHOW_SETTINGS_PANE),
  235. ]),
  236. this._uiGen.createTabPanel('Languages').append([
  237. this._configurationManager.createElement(FILTER_LANGUAGES),
  238. ]),
  239. this._uiGen.createTabPanel('Statistics').append([
  240. this._uiGen.createStatisticsFormGroup(FILTER_GALLERY_TYPES),
  241. this._uiGen.createStatisticsFormGroup(FILTER_LANGUAGES),
  242. this._uiGen.createStatisticsFormGroup(FILTER_TAG_BLACKLIST),
  243. // this._uiGen.createStatisticsFormGroup(FILTER_COMPLEX_TAG_BLACKLIST),
  244. this._uiGen.createSeparator(),
  245. this._uiGen.createStatisticsTotalsGroup(),
  246. ]),
  247. ]),
  248. this._createSettingsFormActions(),
  249. this._uiGen.createSeparator(),
  250. this._uiGen.createStatusSection(),
  251. ])
  252.  
  253. this._onAfterUIBuild = () => {
  254. this._uiGen.getSelectedSection()[0].userScript = this
  255. }
  256. }
  257. }
  258.  
  259. (new HitomiSearchAndUITweaks).init()