eileen12 / Gelbooru Improvements

// ==UserScript==
// @name        Gelbooru Improvements
// @namespace   helo
// @match       http*://*booru.com/index.php
// @version     1.8
// @author      me
// @description 10/4/2021, 7:13:26 PM
// @grant       GM_addStyle
// @grant       GM_xmlhttpRequest
// @grant       GM_registerMenuCommand
// @grant       GM_unregisterMenuCommand
// @grant       GM_setValue
// @grant       GM_getValue
// @require     https://gist.githubusercontent.com/jaideepheer/228d0d0a2a6ea7d01cb02292bd9ffd56/raw/c8373353c880fc9449bd96e71df605c5e18ab96e/tampermonkey_fetch.js
// @require     https://gist.githubusercontent.com/jaideepheer/228d0d0a2a6ea7d01cb02292bd9ffd56/raw/c8373353c880fc9449bd96e71df605c5e18ab96e/rate_limiter.js
// @require     https://gist.github.com/jaideepheer/228d0d0a2a6ea7d01cb02292bd9ffd56/raw/301f1ebca2b84046260abe65fed601d835dffdd8/GM_Cyclic_Config.js
// @license     MIT
// ==/UserScript==

// Set globals
const dark_mode = $.cookie('dark_mode') === '1'
const uid = $.cookie('user_id')
var url = new URL(document.URL);
url.params = Object.fromEntries(url.searchParams.entries());

// Load config
var highres = new GM_Cyclic_Config({
  key:'highres',
  on_update: (...a) => window.location.reload()
});
var highres_mode = new GM_Cyclic_Config({
  key:'highres_mode',
  values:['GIF_ONLY', 'FULL_RES', 'ALL'],
  on_update: (...a) => window.location.reload(),
});
function setpanemode(mode){
  if(mode == 'ON')
    {
      $('.thumbnail-preview').addClass('thumbnail-preview-full')
    }
  if(mode=='OFF'){
    $('.thumbnail-preview').removeClass('thumbnail-preview-full')
  }
}
var pane_mode = new GM_Cyclic_Config({
  key:'pane_mode',
  values:['ON', 'OFF'],
  on_update: (pre,nv,isnext) => setpanemode(nv),
});
setpanemode(pane_mode.value)

// Inject global CSS
var global_style = GM_addStyle(`
.thumbnail-preview-bordered {
  position: relative;
  border-color: transparent;
  border-style: hidden;
  border-width: thin;
  border-style: dashed;
}
.thumbnail-preview-bordered:hover {
  border-color: blue;
}
.fav_button {
  display: none;
  cursor: pointer;
  position: absolute;
  bottom: 0;
  right: 0;
  margin: 5px;
  padding: 2px;
  pointer-events: all;
  height: 10%;
  aspect-ratio: 1;
  background-color: red;
  -webkit-mask: url(layout/heart-fill.svg) no-repeat center;
  -webkit-mask-size: contain;
}
.thumbnail-preview-bordered:hover .fav_button {
  display: block;
}

article.thumbnail-preview video {
  width: 100%;
  height: 100%;
}

article.thumbnail-preview-full {
  max-height: 90%;
  max-width: unset;
  height: 90%;
  width: 90%;
}
article.thumbnail-preview-full img, article.thumbnail-preview-full video {
/*   width: 90vw; */
  height: 90vh;
  max-width: unset;
  max-height: unset;
}
.fav_button {
  height: auto;
  width: 10%;
}

`)

// Fix dark mode notification
if (dark_mode) {
  document.getElementById('notice').style.color = 'black';
}

// ==================
// Helper functions
// ==================

var rate_limiter = new RateLimiter({maxRate:5, timePeriodMillis: 1*1000});

// Add hover overlay
function genFavButton(imgPostID){
  if (uid)
  return $('<div/>')
          .addClass('fav_button')
          .click(() => {post_vote(imgPostID, 'up'); addFav(imgPostID);})
  else return $('<div/>')
}

// Ensure that the <img> tag' ANCESTOR has id='p${imgPostID}'
function applyHighresSrcset(imgPostID){
  rate_limiter.then(() => {
    return tamperFetch(`http://gelbooru.com/index.php?page=dapi&s=post&q=index&id=${imgPostID}&json=1`, {
      responseType: 'blob',
    });
  }).then(data => {
    let fr = new FileReader();
    let rt = new Promise((res, rej)=>{fr.onload=(e)=>res(e.target.result);});
    fr.readAsText(data.response);
    return rt;
  }).then(image_meta => {
    image_meta = JSON.parse(image_meta);
    image_meta = image_meta['post'][0];
    let srcset = '';
    for(c of ['preview', 'sample'])
      {
        if(image_meta[c+'_url'] !== "")
          {
            srcset += `${image_meta[c+'_url']} ${image_meta[c+'_width']}w, `;
          }
      }
    let img = $(`#p${image_meta['id']}`).find('img').first();
    if(image_meta['file_url'].startsWith('https://video'))
      {
        let vd = $(`<video preload="none" controls poster="${image_meta['preview_url']}"><source src="${image_meta['file_url']}"></video>`);
        vd.prop("volume", 0.1);
        img.replaceWith(vd);
      }
    else if(highres.value == 'ON')
      {
        if(highres_mode.value == 'GIF_ONLY')
          {
            if(image_meta['file_url'].endsWith('.gif'))
              img.attr('src', image_meta['file_url']);
          }
        else
          {
            srcset = [
              [image_meta['preview_url'], image_meta['preview_width']],
              [image_meta['sample_url'], image_meta['sample_width']],
              [image_meta['file_url'], image_meta['width']],
            ]
            .filter(([url, width]) => (url != ''))
            .map(([url, width]) => `${url} ${width}w`)
            .slice(
               0, highres_mode.value == 'FULL_RES' ? 3 : 2
             )
            .join(',');
            img.attr('srcset', srcset);
          }
        // Only add gif original images to srcset in GIF_ONLY mode
        // if(highres_mode.value == 'ALL')
        //   {
        //     srcset += `${image_meta['file_url']} ${image_meta['width']}w`;
        //   }
        // else if(highres_mode.value == 'GIF_ONLY' && image_meta['file_url'].endsWith('.gif'))
        //   {
        //     img.attr('src', image_meta['file_url']);
        //     srcset = '';
        //   }
        // // Apply srcset with mid resolution samples
        // img.attr('srcset', srcset);
      }
  });
}

// ==================
// Main Fixes
// ==================

// Apply 'post' related fixes
if (url.params.page === 'post') {
  // Apply post view fixes
  if (url.params.s === 'view') {
    // Fix video player size and preload
    let vplayer = document.getElementById('gelcomVideoPlayer')
    if (vplayer)
      {
        vplayer.style['max-height'] = '90vh';
        vplayer.preload = 'auto';
        // Preload link
        var ll = document.createElement("link");
        ll.href = vplayer.currentSrc;
        ll.as = 'video';
        ll.rel = "preload";
        document.head.appendChild(ll);
      }
    // Resize more like this images
    let mlt = $(".mainBodyPadding div:has(div:contains(More Like This: (Beta Temporary Feature))) img");
    mlt.css({'max-height': 'unset', 'height': '25vh'});
    // highres to all more like this images
    mlt = mlt.parent();
    mlt.wrapAll($('<div/>').css({'display': 'flex', 'flex-wrap': 'wrap'}));
    mlt.each((_,e) => {
      let id = new URL(e.href).searchParams.get('id');
      let rt = $(`<div id="p${id}" class="thumbnail-preview-bordered"></div>`)
      $(e).wrap(rt);
      genFavButton(id).appendTo(e.parentElement);
      applyHighresSrcset(id);
    });
    // Fix page max width
    $('main').css('max-width', '100vw');
  }
  // Apply post list patches
  let rate_limiter = new RateLimiter({maxRate:100, timePeriodMillis: 2*1000});
  if (url.params.s === 'list') {
    // Add hover overlay to all images
    for (post of $('.thumbnail-preview')) {
      let post_id = post.firstElementChild.id.substr(1)
      post = $(post)
      // Apply border
      post.addClass('thumbnail-preview-bordered')
      // Add fav button
      genFavButton(post_id).prependTo(post);
      // Add highres image urls
      applyHighresSrcset(post_id);
    }
  }
}