Sleazy Fork is available in English.

Tsumino Tweaks

Offline tag search support, nhentai/Hentai2Read links on a book information page and click popup disabling

Version vom 09.09.2019. Aktuellste Version

  1. // ==UserScript==
  2. // @name Tsumino Tweaks
  3. // @description Offline tag search support, nhentai/Hentai2Read links on a book information page and click popup disabling
  4. // @namespace xspeed.net
  5. // @version 6
  6. // @icon https://static.nhentai.net/img/logo.650c98bbb08e.svg
  7. // @match *://www.tsumino.com/*
  8. // @grant GM_getValue
  9. // @grant GM_setValue
  10. // @grant GM_xmlhttpRequest
  11. // ==/UserScript==
  12.  
  13. (function() {
  14. 'use strict';
  15. var jsonError = function(data) {
  16. alert(JSON.stringify(data));
  17. }
  18. unsafeWindow.open = function () {
  19. console.error('Blocked window.open', Array.prototype.slice.apply(arguments));
  20. return { }
  21. };
  22.  
  23. unsafeWindow.showModalDialog = function () {
  24. console.error('Blocked window.showModalDialog', Array.prototype.slice.apply(arguments));
  25. return { }
  26. };
  27. String.prototype.removeAfter = function(char) {
  28. var ix = this.indexOf(char);
  29. return ix == -1 ? this : this.substring(0, ix);
  30. }
  31. if (location.href.indexOf('/entry/') != -1) {
  32.  
  33. var title = $('#Title').text().removeAfter('/').removeAfter('|').trim();
  34. var artist = $('a[data-type="Artist"]').text().trim().removeAfter('|').removeAfter('\n').trim();
  35.  
  36. $('#backToIndex').remove();
  37. $('#btnMakeAccount').remove();
  38.  
  39. var onNH = function(resp) {
  40. var respDoc = $(resp.responseText);
  41. var cover = respDoc.find('.cover');
  42. if (cover && cover.attr('href')) {
  43. var dest = 'https://nhentai.net' + cover.attr('href') + '1/';
  44. $('#btnReadOnline').after('<a href="' + dest + '" id="btnReadNH" class="book-read-button button-stack"><i class="fa fa-arrow-circle-right"></i> nhentai</a>');
  45. }
  46. }
  47.  
  48. var onH2R = function(resp) {
  49. console.log(resp.responseText);
  50. var respJson = JSON.parse(resp.responseText);
  51. var suggestions = respJson.response.suggestions;
  52. if (suggestions.length > 0) {
  53. var dest = suggestions[0].slug + '1/';
  54. $('#btnReadOnline').after('<a href="' + dest + '" id="btnReadH2R" class="book-read-button button-stack"><i class="fa fa-arrow-circle-right"></i> Hentai2Read</a>');
  55. }
  56. }
  57.  
  58. var url = 'https://nhentai.net/search/?q=english+' + artist.replace(' ', '+') + '+' + title.replace(/[^a-z0-9+]+/gi, '+');
  59. GM_xmlhttpRequest({ method: "GET", url: url, onload: onNH, onerror: jsonError });
  60.  
  61. GM_xmlhttpRequest({
  62. method: "POST",
  63. url: 'https://hentai2read.com/api',
  64. data: 'controller=search&action=all&query=' + encodeURIComponent(title),
  65. headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
  66. onload: onH2R,
  67. onerror: jsonError
  68. });
  69. }
  70.  
  71. else {
  72.  
  73. var tagSearch = $('#tagDataSearch');
  74. if (tagSearch.length) {
  75.  
  76. var tagMode = false;
  77. var tagData = GM_getValue('tagData', '[]');
  78. var tagList = JSON.parse(tagData);
  79. var oldAdapter = null;
  80.  
  81. var offlineQuery = function(params, callback) {
  82. var res = [];
  83.  
  84. if (params && params.term && params.term.length != 0) {
  85. var term = params.term.toLowerCase();
  86.  
  87. for (var tag of tagList) {
  88. if (tag[0].toLowerCase().indexOf(term) != -1) {
  89. res.push(tag[0]);
  90. if (res.length > 4) break;
  91. }
  92. }
  93. }
  94.  
  95. callback({ results: res.map(x => ({ key: 0, text: x, id: x })) });
  96. };
  97.  
  98. tagSearch.next().html('<button id="tagsRefresh" type="button" class="book-read-button" style="padding: 5px 10px; margin: 5px 0;">Refresh tag list</button> Loaded tags: <span id=tagsCount>0</span>')
  99.  
  100. $('#tagsCount').text(Object.keys(tagList).length);
  101.  
  102. $('#selTagType').change(function() {
  103.  
  104. var selData = $('#selTagValue').data('select2');
  105.  
  106. if ($(this).val() == 1 && !tagMode) {
  107. oldAdapter = selData.dataAdapter.query;
  108. tagMode = true;
  109. selData.dataAdapter.query = offlineQuery;
  110. }
  111. else if (tagMode) {
  112. selData.dataAdapter.query = oldAdapter;
  113. tagMode = false;
  114. }
  115.  
  116. });
  117.  
  118. $('#btnSearch').one('click', function() {
  119. $('#selTagType').change();
  120. });
  121.  
  122. $('#tagsRefresh').click(function() {
  123.  
  124. this.disabled = true;
  125. $('#tagsCount').text('0...');
  126.  
  127. var onload = function(resp) {
  128. var respDoc = JSON.parse(resp.responseText);
  129. tagList = [];
  130.  
  131. for (var i = 0; i < respDoc.length; ++i) {
  132. if (respDoc[i].type == "Tag") {
  133. var id = respDoc[i].text.trim();
  134. if (id.length > 0) tagList.push([id, 1]);
  135. }
  136. }
  137. //respDoc.find('.blog-all-post>.row-no-margin>.col-no-padding>.definition-section:nth(1)').next().find('.definition-key').each(function() {
  138. // var id = $(this).text().trim();
  139. // if (id.length > 0) tagList.push([id, 1]);
  140. //});
  141.  
  142. $('#tagsCount').text(Object.keys(tagList).length);
  143. GM_setValue('tagData', JSON.stringify(tagList));
  144.  
  145. $('#tagsRefresh').prop('disabled', false);
  146.  
  147. };
  148.  
  149. GM_xmlhttpRequest({ method: "GET", url: 'https://core.tsumino.com/api/Tag/GetAllDefinableTagsAsync', onload: onload, onerror: jsonError });
  150.  
  151. });
  152. }
  153. }
  154. })();