Tsumino Tweaks

Offline tag search support and nhentai/Hentai2Read links on a book information page

As of 2019-06-13. See the latest version.

// ==UserScript==
// @name        Tsumino Tweaks
// @description Offline tag search support and nhentai/Hentai2Read links on a book information page
// @namespace   xspeed.net
// @version     3
// @icon        https://static.nhentai.net/img/logo.650c98bbb08e.svg
// @match       *://www.tsumino.com/*
// @grant       GM_getValue
// @grant       GM_setValue
// @grant       GM_xmlhttpRequest
// ==/UserScript==

(function() {
  'use strict';
  
  var jsonError = function(data) {
    alert(JSON.stringify(data));
  }
  
  if (location.href.indexOf('/Book/Info/') != -1) {
    
    var title = $('#Title').text().trim();
    var artist = $('a[data-define="Dagashi"]').text().trim();
    
    var ix = title.indexOf('/');
    if (ix != -1) {
      title = title.substring(0, ix);
    }
    
    ix = title.indexOf('|');
    if (ix != -1) {
      title = title.substring(0, ix);
    }
    
    $('#backToIndex').remove();
    $('#btnMakeAccount').remove();
    
    var onNH = function(resp) {
      var respDoc = $(resp.responseText);
      var cover = respDoc.find('.cover');
      if (cover && cover.attr('href')) {
        var dest = 'https://nhentai.net' + cover.attr('href') + '1/';
        $('#btnReadOnline').after('<a href="' + dest + '" id="btnReadNH" class="book-read-button button-stack"><i class="fa fa-arrow-circle-right"></i> nhentai</a>');
      }
    }
    
    var onH2R = function(resp) {
      console.log(resp.responseText);
      var respJson = JSON.parse(resp.responseText);
      var suggestions = respJson.response.suggestions;
      if (suggestions.length > 0) {
        var dest = suggestions[0].slug + '1/';
        $('#btnReadOnline').after('<a href="' + dest + '" id="btnReadH2R" class="book-read-button button-stack"><i class="fa fa-arrow-circle-right"></i> Hentai2Read</a>');
      }
    }
    
    var url = 'https://nhentai.net/search/?q=english+' + artist + '+' + title.replace(/[^a-z0-9+]+/gi, '+');
    GM_xmlhttpRequest({ method: "GET", url: url, onload: onNH, onerror: jsonError });
    
    GM_xmlhttpRequest({
      method: "POST",
      url: 'https://hentai2read.com/api',
      data: 'controller=search&action=all&query=' + encodeURIComponent(title),
      headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
      onload: onH2R,
      onerror: jsonError
    });
  }
  
  else {
    
    var tagSearch = $('#tagDataSearch');
    if (tagSearch.length) {
      
      var tagMode = false;
      var tagData = GM_getValue('tagData', '[]');
      var tagList = JSON.parse(tagData);
      var oldAdapter = null;
      
      var offlineQuery = function(params, callback) {
        var res = [];
      
        if (params && params.term && params.term.length != 0) {
          var term = params.term.toLowerCase();
          
          for (var tag of tagList) {
            if (tag[0].toLowerCase().indexOf(term) != -1) {
              res.push(tag[0]);
              if (res.length > 4) break;
            }
          }
        }
        
        callback({ results: res.map(x => ({ key: 0, text: x, id: x })) });
      };
      
      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>')
      
      $('#tagsCount').text(Object.keys(tagList).length);

      $('#selTagType').change(function() {
        
        var selData = $('#selTagValue').data('select2');
        
        if ($(this).val() == 1 && !tagMode) {
          oldAdapter = selData.dataAdapter.query;
          tagMode = true;
          selData.dataAdapter.query = offlineQuery;
        }
        else if (tagMode) {
          selData.dataAdapter.query = oldAdapter;
          tagMode = false;
        }
        
      });
      
      setTimeout(function() {
        $('#selTagValue').data('select2').dataAdapter.query = offlineQuery;
        tagMode = true;
      }, 1000);
      
      $('#tagsRefresh').click(function() {
        
        this.disabled = true;
        $('#tagsCount').text('0...')
        
        var parseTable = function(doc) {
          doc.find('.table.table-striped').find('tr').each(function() {
            var row = $(this).children('td');
            var id = row.first().text().trim();
            if (id.length > 0) tagList.push([id, parseInt(row.eq(1).text())]);
          });
        };
        
        var onload = function(resp) {
          var respDoc = $(resp.responseText);
          
          tagList = [];
          
          var hrefSet = new Set();
          respDoc.find('a[href*="/Tags/Index/1/"]').each(function() {
            var ix = this.href.indexOf('?');
            if (ix != -1) hrefSet.add(this.href);
          });
          hrefSet = Array.from(hrefSet);
          
          var process = function(resp) {
            parseTable($(resp.responseText));
            
            var next = hrefSet.pop();
            if (next) {
              $('#tagsCount').text(Object.keys(tagList).length + '...');
              
              GM_xmlhttpRequest({ method: "GET", url: next, onload: process, onerror: jsonError });
            }
            else {
              tagList.sort((a, b) => b[1] - a[1]);
              
              $('#tagsCount').text(Object.keys(tagList).length);
              GM_setValue('tagData', JSON.stringify(tagList));

              $('#tagsRefresh').prop('disabled', false);
            }
          };
          
          process(resp);
          
        };
        
        GM_xmlhttpRequest({ method: "GET", url: 'https://www.tsumino.com/Tags', onload: onload, onerror: jsonError });
        
      });
    }
  }
})();