Sleazy Fork is available in English.

Image Board Enhancer (Rule34, Gelbooru, Konachan, and more)

Auto Resize images and video on multiple image boards.

Verze ze dne 01. 06. 2020. Zobrazit nejnovější verzi.

// ==UserScript==
// @name         Image Board Enhancer (Rule34, Gelbooru, Konachan, and more)
// @namespace    ImageBoardEnhancer
// @version      0.7
// @description  Auto Resize images and video on multiple image boards.
// @author       DanDanDan
// @match        *://rule34.xxx/*
// @match        *://chan.sankakucomplex.com/*
// @match        *://idol.sankakucomplex.com/*
// @match        *://gelbooru.com/*
// @match        *://danbooru.donmai.us/*
// @match        *://konachan.com/*
// @match        *://yande.re/*
// @match        *://safebooru.org/*
// @match        *://rule34.paheal.net/*
// @match        *://rule34hentai.net/*
// @require      https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js
// @grant       GM.setValue
// @grant       GM.getValue
// ==/UserScript==

(async () => {
  'use strict';

  var resizeImageToFit = await GM.getValue('resizeImageToFit', true);
  var resizeVideoToFit = await GM.getValue('resizeVideoToFit', true);
  var autoplayVideos = await GM.getValue('autoplayVideos', true);
  var autoScrollToContent = await GM.getValue('autoScrollToContent', true);
  var updateWithWindowResize = await GM.getValue('updateWithWindowResize', true);
  var showFitButton = await GM.getValue('showFitButton', true);
  var showScrollButton = await GM.getValue('showScrollButton', true);
  var showR34XXXLikeAndFavoriteButtons = await GM.getValue('showR34XXXLikeAndFavoriteButtons', true);
  var removeFluid = await GM.getValue('removeFluid', false);
  var videoVolume = await GM.getValue('videoVolume', 0);
  var widthMargin = await GM.getValue('widthMargin', 15);
  var heightMargin = await GM.getValue('heightMargin', 15);

  // Create variables.
  var currentWindowWidth = 0;
  var currentWindowHeight = 0;
  var currentWindowAspect = 0;
  var contentTrueWidth = 0;
  var contentTrueHeight = 0;
  var contentTrueAspect = 0;
  var resizeReady = false;
  var debugMode = true;
  var r34buttons = false;
  var toolbarDOM = '.sidebar form';
  var containerDOM = '#content';
  var imageDOM = '#image';
  var playerDOM;

  // Per-site DOM selection.
  if (document.location.hostname.toLowerCase() == 'rule34.xxx') { toolbarDOM = '.space'; r34buttons = showR34XXXLikeAndFavoriteButtons; playerDOM = '#gelcomVideoContainer'; };
  if (document.location.hostname.toLowerCase() == 'chan.sankakucomplex.com') { toolbarDOM = '#search-form'; }
  if (document.location.hostname.toLowerCase() == 'idol.sankakucomplex.com') { toolbarDOM = '#search-form'; }
  if (document.location.hostname.toLowerCase() == 'gelbooru.com') { toolbarDOM = '#tag-list form'; containerDOM = '.contain-push'; }
  if (document.location.hostname.toLowerCase() == 'danbooru.donmai.us') { toolbarDOM = '#search-box'; }
  if (document.location.hostname.toLowerCase() == 'rule34.paheal.net') { toolbarDOM = '#Navigationleft'; containerDOM = 'article'; imageDOM = '#main_image'; }
  if (document.location.hostname.toLowerCase() == 'rule34hentai.net') { toolbarDOM = '#Navigationleft'; containerDOM = 'article'; imageDOM = '#main_image'; playerDOM = '#fluid_video_wrapper_video-id'; }

  // Remove the Gelcom Video player.
  function removeFluidPlayer() {
    if (debugMode) console.log('removeFluidPlayer');
    $(playerDOM).replaceWith($(containerDOM + ' video'));
    $(containerDOM + ' video').attr('id', 'image');
    document.getElementById('image').outerHTML = document.getElementById('image').outerHTML; // This removes all event listeners, it seems jquery tries to maintain  them.
    $(containerDOM + ' video').removeAttr('style');
    $(containerDOM + ' video').removeAttr('playsinline');
    $(containerDOM + ' video').removeAttr('webkit-playsinline');
    $(containerDOM + ' video').attr('controls', 'true');
    $(containerDOM + ' video').attr('autoplay', autoplayVideos);
  }

  // Get window size and aspect ratio.
  function getWindowProps() {
    if (debugMode) console.log('getWindowProps');
    currentWindowWidth = $(window).width() - widthMargin;
    currentWindowHeight = $(window).height() - heightMargin;
    if (currentWindowWidth != 0 && currentWindowHeight != 0)
      currentWindowAspect = currentWindowWidth / currentWindowHeight;
  }

  // Get the real size of the video or image.
  function getContentProps() {
    if (debugMode) console.log('getContentProps');

    if ($(containerDOM + ' video').length) {
      contentTrueWidth = $(containerDOM + ' video')[0].videoWidth;
      contentTrueHeight = $(containerDOM + ' video')[0].videoHeight;
    }

    else if ($(containerDOM + ' ' + imageDOM).length) {
      var screenImage = $(containerDOM + ' ' + imageDOM);
      var theImage = new Image();
      theImage.src = screenImage.attr("src");
      contentTrueWidth = theImage.width;
      contentTrueHeight = theImage.height;
    }

    if (contentTrueWidth != 0 && contentTrueHeight != 0)
      contentTrueAspect = contentTrueWidth / contentTrueHeight;
    resizeReady = true;
  }

  // Resize the image (This resizes the video on some sites eg. sankakucomplex.com)
  function resizeImage() {
    if (debugMode) console.log('resizeImage');

    $(containerDOM + ' ' + imageDOM).css('max-width', '');

    if (currentWindowAspect > contentTrueAspect) {
      $(containerDOM + ' ' + imageDOM)[0].width = currentWindowHeight * contentTrueAspect;
      $(containerDOM + ' ' + imageDOM)[0].height = currentWindowHeight;
    }

    else {
      $(containerDOM + ' ' + imageDOM)[0].width = currentWindowWidth;
      $(containerDOM + ' ' + imageDOM)[0].height = currentWindowWidth / contentTrueAspect;
    }

    // Remove css from images.
    $(containerDOM + ' ' + imageDOM).removeAttr('style');
  }

  // Resize Fluid video player.
  function resizeFluidVideo() {
    if (debugMode) console.log('resizeFluidVideo');

    $(containerDOM + ' ' + playerDOM).css('max-width', '');

    if (currentWindowAspect > contentTrueAspect) {
      $(containerDOM + ' ' + playerDOM).css('width', currentWindowHeight * contentTrueAspect);
      $(containerDOM + ' ' + playerDOM).css('height', currentWindowHeight);
    }

    else {
      $(containerDOM + ' ' + playerDOM).css('width', currentWindowWidth);
      $(containerDOM + ' ' + playerDOM).css('height', currentWindowWidth / contentTrueAspect);
    }
  }

  // Resize default video.
  function resizeVideo() {
    if (debugMode) console.log('resizeVideo');

    $(containerDOM + ' video').css('max-width', '');

    if (currentWindowAspect > contentTrueAspect) {
      $(containerDOM + ' video')[0].width = currentWindowHeight * contentTrueAspect;
      $(containerDOM + ' video')[0].height = currentWindowHeight;
    }

    else {
      $(containerDOM + ' video')[0].width = currentWindowWidth;
      $(containerDOM + ' video').height = currentWindowWidth / contentTrueAspect;
    }
  }

  // Scroll the window to the video or image.
  function scrollToContent() {
    if (debugMode) console.log('scrollToContent');

    var contentID;

    if ($(containerDOM + ' ' + imageDOM).length) contentID = containerDOM + ' ' + imageDOM;
    else if ($(containerDOM + ' ' + playerDOM).length) contentID = containerDOM + ' ' + playerDOM;
    else if ($(containerDOM + ' video').length) contentID = containerDOM + ' video';

    $([document.documentElement, document.body]).animate({
      scrollTop: $(contentID).offset().top
    }, 0);
    $([document.documentElement, document.body]).animate({
      scrollLeft: $(contentID).offset().left
    }, 0);
  }

  // Check if resize is ready and what type of content to resize. 
  function fitContent() {
    if (debugMode) console.log('fitContent');

    if (resizeReady) {
      getWindowProps();
      if ($(containerDOM + ' ' + imageDOM).length) {
        resizeImage();
      }

      else if ($(containerDOM + ' ' + playerDOM).length) {
        resizeFluidVideo();
      }

      else if ($(containerDOM + ' video').length) {
        resizeVideo();
      }
    }

  }

  // Set the video auto play and volume settings.
  function videoSettings() {
    if (debugMode) console.log('videoSettings');

    $(containerDOM + ' video').prop('autoplay', autoplayVideos);
    $(containerDOM + ' video').prop('volume', videoVolume);
    $(containerDOM + ' video').prop('loop', true);
    if (autoplayVideos) $(containerDOM + ' video')[0].play(); else $(containerDOM + ' video')[0].pause();
  }

  // Remove the Gelcom player if present.
  if (removeFluid && $(playerDOM).length) removeFluidPlayer();

  // Get the image properties, resize, and scroll as the page is loading. 
  // If the image loads too quickly it wont fire the event.
  if ($(containerDOM + ' video').length || $(containerDOM + ' ' + imageDOM).length) {
    getContentProps();
    if (resizeImageToFit) fitContent();
    if (autoScrollToContent) scrollToContent();
  }

  // Add event listener to the image or video.
  if ($(containerDOM + ' video').length) {
    if (debugMode) console.log('Create video event listener');

    videoSettings();
    $(containerDOM + ' video').on('loadedmetadata', function () { //NOTE: replaced 'loadedmetadata' with 'canplay'
      getContentProps();
      if (resizeVideoToFit) fitContent();
      if (autoScrollToContent) scrollToContent();
      $(containerDOM + ' video').play();
    });
  }

  else if ($(containerDOM + ' ' + imageDOM).length) {
    if (debugMode) console.log('Create image event listener');

    $(containerDOM + ' ' + imageDOM).on('load', function () {
      getContentProps();
      if (resizeImageToFit) fitContent();
      if (autoScrollToContent) scrollToContent();
    });
  }

  // Add the event listener to the window.
  if (updateWithWindowResize) {
    $(window).resize(function () {
      fitContent();
    });
  }

  // Setting Functions
  function showSettings() {
    $("#ibenhancerSettings").addClass('show');
  }
  function hideSettings() {
    $("#ibenhancerSettings").removeClass('show');
  }
  function saveSettings() {
      GM.setValue('resizeImageToFit', $('#resizeImageToFitCheckbox').is(':checked'));

      GM.setValue('resizeVideoToFit', $('#resizeVideoToFitCheckbox').is(':checked'));

      GM.setValue('autoplayVideos', $('#autoplayVideosCheckbox').is(':checked'));

      GM.setValue('autoScrollToContent', $('#autoScrollToContentCheckbox').is(':checked'));

      GM.setValue('updateWithWindowResize', $('#updateWithWindowResizeCheckbox').is(':checked'));

      GM.setValue('showFitButton', $('#showFitButtonCheckbox').is(':checked'));

      GM.setValue('showScrollButton', $('#showScrollButtonCheckbox').is(':checked'));

      GM.setValue('showR34XXXLikeAndFavoriteButtons', $('#showR34XXXLikeAndFavoriteButtonsCheckbox').is(':checked'));

      GM.setValue('removeFluid', $('#removeFluidCheckbox').is(':checked'));

      GM.setValue('videoVolume', $('#videoVolumeInput').val());
      GM.setValue('widthMargin', $('#widthMarginInput').val());
      GM.setValue('heightMargin', $('#heightMarginInput').val());

    hideSettings();
  }

  // Create the toolbar.
  if ($(containerDOM + ' ' + imageDOM).length || $(containerDOM + ' video').length) {
    if (debugMode) console.log('Create toolbar');

    $(toolbarDOM).after("<div id='ibenhancer' style=''>Image Board Enhancer<br></div>");

    if (showFitButton) {
      $("#ibenhancer").append("<button id='fitContentButton' style='margin-top: 3px; background: #fff; border: 1px solid #dadada; width: 50px;'>Fit</button>");
      $("#fitContentButton").click(function () { getContentProps(); fitContent() });
    }

    if (showScrollButton) {
      $("#ibenhancer").append("<button id='scrollContentButton' style='margin-top: 3px; background: #fff; border: 1px solid #dadada; width: 60px;'>Scroll</button>");
      $("#scrollContentButton").click(scrollToContent);
    }

    // Create settings.
    $("#ibenhancer").append("<br><button id='ibenhancerSettingsButton' style='margin-top: 3px; background: #fff; border: 1px solid #dadada; width: 120px;'>Settings</button>");
    $("#ibenhancerSettingsButton").click(showSettings);
    $("#ibenhancer").append(`
      <div id="ibenhancerSettings">
        <label><input id="resizeImageToFitCheckbox" type="checkbox" ` + (resizeImageToFit ? `checked` : ``) + `>resizeImageToFit</label>
        <br>
        <label><input id="resizeVideoToFitCheckbox" type="checkbox" ` + (resizeVideoToFit ? `checked` : ``) + `>resizeVideoToFit</label>
        <br>
        <label><input id="autoplayVideosCheckbox" type="checkbox" ` + (autoplayVideos ? `checked` : ``) + `>autoplayVideos</label>
        <br>
        <label><input id="autoScrollToContentCheckbox" type="checkbox" ` + (autoScrollToContent ? `checked` : ``) + `>autoScrollToContent</label>
        <br>
        <label><input id="updateWithWindowResizeCheckbox" type="checkbox" ` + (updateWithWindowResize ? `checked` : ``) + `>updateWithWindowResize</label>
        <br>
        <label><input id="showFitButtonCheckbox" type="checkbox" ` + (showFitButton ? `checked` : ``) + `>showFitButton</label>
        <br>
        <label><input id="showScrollButtonCheckbox" type="checkbox" ` + (showScrollButton ? `checked` : ``) + `>showScrollButton</label>
        <br>
        <label><input id="showR34XXXLikeAndFavoriteButtonsCheckbox" type="checkbox" ` + (showR34XXXLikeAndFavoriteButtons ? `checked` : ``) + `>showR34XXXLikeAndFavoriteButtons</label>
        <br>
        <label><input id="removeFluidCheckbox" type="checkbox" ` + (removeFluid ? `checked` : ``) + `>removeFluid</label>
        <br>
        <label>videoVolume<input id="videoVolumeInput" type="number" min="0" max="1" step="0.01" value="` + videoVolume + `" style="width:60px;">0 - 1</label>
        <br>
        <label>widthMargin<input id="widthMarginInput" type="number" min="0" max="100" step="1" value="` + widthMargin + `" style="width:60px;">0 - 100</label>
        <br>
        <label>heightMargin<input id="heightMarginInput" type="number" min="0" max="100" step="1" value="` + heightMargin + `" style="width:60px;">0 - 100</label>
        <br>
        <button id="ibenhancerSettingsSave">Save</button><button id="ibenhancerSettingsCancel">Cancel</button>
      </div>
    ` );
    $("#ibenhancerSettingsSave").click(saveSettings);
    $("#ibenhancerSettingsCancel").click(hideSettings);
    addGlobalStyle(`
        #ibenhancerSettings {
          position: fixed;
          width: 300px;
          height: 320px;
          left: calc(50vw - 155px);
          top: calc(50vh - 165px);
          background-color: white;
          border: 2px solid black;
          border-radius: 3px;
          overflow-y: auto;
          overflow-x: hidden;
          padding: 10px;
          text-align: left;
          z-index: 999999;
          display: none;
          -webkit-box-sizing: unset;
          -moz-box-sizing: unset;
          box-sizing: unset;
        }
        #ibenhancerSettings * {
          all: ini;
          font: 15px Arial, sans-serif;
        }
        #ibenhancerSettings input {
          margin: 5px;
          width: auto;
       }
        #ibenhancerSettings.show {
          display: block;
        }
			`);

    // Add the like and favorite button to rule34.xxx
    if (r34buttons) {
      if (debugMode) console.log('r34buttons');

      $("#ibenhancer").append('<br><img id="like-butt" class="custom-button" src="https://i.imgur.com/Kh1HzGr.png" alt="like"><img id="favorite-butt" class="custom-button" src="https://i.imgur.com/dTpBrIj.png" alt="favorite">');
      $("#like-butt").click(function () {
        $("#stats > ul > li:contains('(vote up)') > a:contains('up')").click();
      });
      $("#favorite-butt").click(function () {
        $("#stats + div > ul > li > a:contains('Add to favorites')").click();
      });

      addGlobalStyle(`
				img.custom-button {
					cursor: pointer;
					width: 35px;
					padding: 3px;
					margin: 0;
					border-radius: 20px;
				}
				.custom-button:hover {
					background-color: rgba(255,255,255,.5);
				}
				.custom-button:active {
					background-color: rgba(255,255,255,1);
        }
			`);
    }
  }

  function addGlobalStyle(css) {
    if (debugMode) console.log('addGlobalStyle');
    var head, style;
    head = document.getElementsByTagName('head')[0];
    if (!head) { return; }
    style = document.createElement('style');
    style.type = 'text/css';
    style.innerHTML = css;
    head.appendChild(style);
  }



  if (debugMode) console.log('End of script.');
})();