PornHub Plus

A kinder PornHub. Because you're worth it.

// ==UserScript==
// @author      LD
// @version     1.0
// @name        PornHub Plus
// @description A kinder PornHub. Because you're worth it.
// @namespace   LD
// @date        2018-08-08
// @include     *pornhub.com/*
// @run-at      document-start
// @grant       none
// @license     Public Domain
// @icon        http://www.techthisoutnews.com/wp-content/uploads/2018/03/porn.jpg
// @grant       GM_addStyle
// ==/UserScript==

(() => {
  const OPTIONS = {
    openWithoutPlaylist: true,
    showOnlyVerified: JSON.parse(localStorage.getItem('plus_showOnlyVerified')) || false,
    redirectToVideos: JSON.parse(localStorage.getItem('plus_redirectToVideos')) || false,
    autoresizePlayer: JSON.parse(localStorage.getItem('plus_autoresizePlayer')) || false,
    durationFilter: JSON.parse(localStorage.getItem('plus_durationFilter')) || { max: 0, min: 0 }
  }
  
  /* Styles - Shared between all "Plus" userscripts */
  
  const sharedStyles = `
    /* Our own elements */

    .plus-buttons {
      background: rgba(27, 27, 27, 0.9);
      box-shadow: 0px 0px 12px rgba(20, 111, 223, 0.9);
      font-size: 12px;
      position: fixed;
      bottom: 10px;
      padding: 10px 22px 8px 24px;
      right: 0;
      z-index: 100;
      transition: all 0.3s ease;

      /* Negative margin-right calculated later based on width of buttons */
    }

    .plus-buttons:hover {
      box-shadow: 0px 0px 3px rgba(0, 0, 0, 0.3);
    }

    .plus-buttons .plus-button {
      margin: 10px 0;
      padding: 6px 15px;
      border-radius: 4px;
      font-weight: 700;
      display: block;
      position: relative;
      text-align: center;
      vertical-align: top;
      cursor: pointer;
      border: none;
      text-decoration: none;
    }

    .plus-buttons a.plus-button {
      background: rgb(221, 221, 221);
      color: rgb(51, 51, 51);
    }

    .plus-buttons a.plus-button:hover {
      background: rgb(187, 187, 187);
      color: rgb(51, 51, 51);
    }

    .plus-buttons a.plus-button.plus-button-isOn {
      background: rgb(20, 111, 223);
      color: rgb(255, 255, 255);
    }

    .plus-buttons a.plus-button.plus-button-isOn:hover {
      background: rgb(0, 91, 203);
      color: rgb(255, 255, 255);
    }

    .plus-hidden {
      display: none !important;
    }
  `;
  
  /* Styles - Color theme */
  
  const themeStyles = `
    .plus-buttons {
      box-shadow: 0px 0px 12px rgba(255, 153, 0, 0.85);
    }

    .plus-buttons:hover {
      box-shadow: 0px 0px 3px rgba(0, 0, 0, 0.3);
    }

    .plus-buttons a.plus-button {
      background: rgb(47, 47, 47);
      color: rgb(172, 172, 172);
    }

    .plus-buttons a.plus-button:hover {
      background: rgb(79, 79, 79);
      color: rgb(204, 204, 204);
    }

    .plus-buttons a.plus-button.plus-button-isOn {
      background: rgb(255, 153, 0);
      color: rgb(0, 0, 0);
    }

    .plus-buttons a.plus-button.plus-button-isOn:hover {
      background: rgb(255, 153, 0);
      color: rgb(255, 255, 255);
    }
  `;
  
  /* Styles - General site-specific */
  
  const generalStyles = `
    /* Hide elements */

    .abovePlayer,
    .streamatesModelsContainer,
    #headerUpgradePremiumBtn,
    #headerUploadBtn,
    #PornhubNetworkBar,
    #js-abContainterMain,
    #hd-rightColVideoPage > :not(#relatedVideosVPage) {
      display: none !important;
    }

    /* Show all playlists without scrolling in "add to" */

    .slimScrollDiv {
      height: auto !important;
    }

    #scrollbar_watch {
      max-height: unset !important;
    }

    /* Hide premium video from related videos sidebar */

    #relateRecommendedItems li:nth-of-type(5) {
      display: none !important;
    }

    /* Prevent animating player size change on each page load */

    #main-container .video-wrapper #player.wide {
      transition: none !important;
    }
  `;
  
  /*
   * Run after page has loaded
   */
  
  window.addEventListener('DOMContentLoaded', () => {
    const player = document.querySelector('#player');
    const video = document.querySelector('video');
    // const largePlayerButton = document.querySelector('.mhp1138_cinema');
    
    /*
     * Use wide player by default
     */
    
    if (player && OPTIONS.autoresizePlayer) {
      player.classList.remove('original');
      player.classList.add('wide');
      document.querySelector('#hd-rightColVideoPage').classList.add('wide');
      
      setTimeout(() => {
        document.querySelector('.mhp1138_cinema').classList.add('mhp1138_active');
      }, 2000);
      
      // if (video.readyState >= 2) {
      //   // Video cached and ready
      //   largePlayerButton.dispatchEvent(new MouseEvent('mouseup'));
      // } else {
      //   // Wait for video to be ready
      //   video.addEventListener('canplay', function onCanPlay() {
      //     /* Click large player button */
      //     largePlayerButton.dispatchEvent(new MouseEvent('mouseup'));
      // 
      //     /* Only run once */
      //     video.removeEventListener('canplay', onCanPlay, false);
      //   });
      // }
    }
    
    /*
     * Clicking a video on a playlist page opens it without the playlist at the top
     * 
     * If this option is disabled, add it as a link after the video title.
     */

    if (OPTIONS.openWithoutPlaylist) {
      const playlistLinks = document.querySelectorAll('#playlistWrapper #videoPlaylist li a');
      for (link of playlistLinks) link.href = link.href.replace('pkey', 'nopkey');
    } else {
      const links = document.querySelectorAll('#playlistWrapper #videoPlaylist li .title a');
    
      for (link of links) {
        let newLink = document.createElement('a');
        newLink.href = link.href.replace('pkey', 'nopkey');
        newLink.appendChild(document.createTextNode('[↗]'));
        link.parentNode.appendChild(newLink);
      }
    }
    
    /*
     * Allow scrolling the page when mouse hovers playlists in "add to"
     */
    
    /* Clone playlist scroll container to remove listeners that preventDefault() */
    var scrollContainer = document.getElementById('scrollbar_watch');
    
    if (scrollContainer) {
      var newScrollContainer = scrollContainer.cloneNode(true);
      
      scrollContainer.parentNode.replaceChild(newScrollContainer, scrollContainer);
    }
    
    /*
     * Automatically "load more" to show all videos on user video pages
     
    
    var autoloadTimer = null; 
    var loadMoreButton = document.getElementById('moreDataBtn');
    
    function autoloadMore() {
      if (loadMoreButton.style.display === 'none') {
        clearInterval(autoloadTimer);
        
      } else {
        loadMoreButton.onclick();
      }
    }
    
    if (loadMoreButton) {
      autoloadTimer = setInterval(autoloadMore, 5000);
    }*/
    
    /*
     * Add buttons for certain options
     */
    
    /* Buttons container */
    
    let buttons = document.createElement('div');
    let scrollButton = document.createElement('a');
    let scrollButtonText = document.createElement('span');
    let verifiedButton = document.createElement('a');
    let verifiedButtonText = document.createElement('span');
    let verifiedButtonState = OPTIONS.showOnlyVerified ? 'plus-button-isOn' : 'plus-button-isOff';
    let redirectToVideosButton = document.createElement('a');
    let redirectToVideosButtonText = document.createElement('span');
    let redirectToVideosButtonState = OPTIONS.redirectToVideos ? 'plus-button-isOn' : 'plus-button-isOff';
    let autoresizeButton = document.createElement('a');
    let autoresizeButtonText = document.createElement('span');
    let autoresizeButtonState = OPTIONS.autoresizePlayer ? 'plus-button-isOn' : 'plus-button-isOff';
    let durationShortButton = document.createElement('a');
    let durationShortButtonText = document.createElement('span');
    let durationShortButtonState = !OPTIONS.durationFilter.min ? 'plus-button-isOn' : 'plus-button-isOff';
    
    buttons.classList.add('plus-buttons');
    
    scrollButtonText.textContent = "Scroll to top";
    scrollButtonText.classList.add('text');
    scrollButton.appendChild(scrollButtonText);
    scrollButton.classList.add('plus-button');
    scrollButton.addEventListener('click', () => {
      window.scrollTo({ top: 0 });
    });
    
    buttons.appendChild(scrollButton);
    
    verifiedButtonText.textContent = 'Show only verified';
    verifiedButtonText.classList.add('text');
    verifiedButton.appendChild(verifiedButtonText);
    verifiedButton.classList.add(verifiedButtonState, 'plus-button');
    verifiedButton.addEventListener('click', () => {
      setShowOnlyVerified(!OPTIONS.showOnlyVerified);
    });
    
    buttons.appendChild(redirectToVideosButton);
    
    redirectToVideosButtonText.textContent = 'Redirect profiles to uploads';
    redirectToVideosButtonText.classList.add('text');
    redirectToVideosButton.appendChild(redirectToVideosButtonText);
    redirectToVideosButton.classList.add(redirectToVideosButtonState, 'plus-button');
    redirectToVideosButton.addEventListener('click', () => {
      OPTIONS.redirectToVideos = !OPTIONS.redirectToVideos;
      localStorage.setItem('plus_redirectToVideos', OPTIONS.redirectToVideos);
      
      if (OPTIONS.redirectToVideos) {
        redirectToVideosButton.classList.replace('plus-button-isOff', 'plus-button-isOn');
      } else {
        redirectToVideosButton.classList.replace('plus-button-isOn', 'plus-button-isOff');
      }
    });
    
    buttons.appendChild(autoresizeButton);
    
    durationShortButtonText.textContent = 'Show short videos (< 8 min)';
    durationShortButtonText.classList.add('text');
    durationShortButton.appendChild(durationShortButtonText);
    durationShortButton.classList.add(durationShortButtonState, 'plus-button');
    durationShortButton.addEventListener('click', () => {
      OPTIONS.durationFilter.min = OPTIONS.durationFilter.min ? 0 : 8;
      localStorage.setItem('plus_durationFilter', JSON.stringify(OPTIONS.durationFilter));
      
      if (!OPTIONS.durationFilter.min) {
        durationShortButton.classList.replace('plus-button-isOff', 'plus-button-isOn');
        updateDurationFilter();
      } else {
        durationShortButton.classList.replace('plus-button-isOn', 'plus-button-isOff');
        updateDurationFilter();
      }
    });
    
    buttons.appendChild(durationShortButton);
    
    autoresizeButtonText.textContent = 'Auto-resize player';
    autoresizeButtonText.classList.add('text');
    autoresizeButton.appendChild(autoresizeButtonText);
    autoresizeButton.classList.add(autoresizeButtonState, 'plus-button');
    autoresizeButton.addEventListener('click', () => {
      OPTIONS.autoresizePlayer = !OPTIONS.autoresizePlayer;
      localStorage.setItem('plus_autoresizePlayer', OPTIONS.autoresizePlayer);
      
      if (OPTIONS.autoresizePlayer) {
        autoresizeButton.classList.replace('plus-button-isOff', 'plus-button-isOn');
      } else {
        autoresizeButton.classList.replace('plus-button-isOn', 'plus-button-isOff');
      }
    });
    
    document.body.appendChild(buttons);
    
    if (window.location.href.includes('/playlist/')) {
      buttons.appendChild(verifiedButton);

      setTimeout(() => {
        setShowOnlyVerified(OPTIONS.showOnlyVerified);
      }, 1000);
    }
    
    /* Redirect profile page to all uploads, except if we just came from there */
    
    if (
      /^https:\/\/www\.pornhub\.com\/pornstar\/([^\/]+)$/.test(window.location.href) ||
      /^https:\/\/www\.pornhub\.com\/model\/([^\/]+)$/.test(window.location.href) ||
      /^https:\/\/www\.pornhub\.com\/users\/([^\/]+)$/.test(window.location.href) ||
      /^https:\/\/www\.pornhub\.com\/channels\/([^\/]+)$/.test(window.location.href)) {
      if (OPTIONS.redirectToVideos && !/.+\/videos\/upload.*/.test(document.referrer)) {
        window.location.href = window.location.href + '/videos/upload';
      }
    }
    
    function setShowOnlyVerified(state) {
      OPTIONS.showOnlyVerified = state;
      localStorage.setItem('plus_showOnlyVerified', state);
      
      if (state) {
        verifiedButton.classList.replace('plus-button-isOff', 'plus-button-isOn');
      } else {
        verifiedButton.classList.replace('plus-button-isOn', 'plus-button-isOff');
      }
      
      document.querySelectorAll('.videoBox').forEach((box) => {
        if (!box.innerHTML.includes('Video of verified member')) {
          if (state) {
            box.classList.add('plus-hidden');
          } else {
            box.classList.remove('plus-hidden');
          }
        }
      });
    }
    
    function updateDurationFilter() {
      document.querySelectorAll('.videoBox').forEach((box) => {
        let durationParts = box.querySelector('.duration').textContent.split(":");
        let duration = { minutes: parseInt(durationParts[0]), seconds: parseInt(durationParts[1]) }
        
        if (duration.minutes >= OPTIONS.durationFilter.min &&
            (!OPTIONS.durationFilter.max || duration.minutes <= OPTIONS.durationFilter.max)) {
          box.classList.remove('plus-hidden');
        } else {
          box.classList.add('plus-hidden');
        }
      });
    }
    
    let loadMoreButton = document.querySelector('.more_related_btn');
    
    if (loadMoreButton)
      loadMoreButton.addEventListener('click', updateDurationFilter);
    
    updateDurationFilter();
    
    /*
     * Add styles
     */
    
    GM_addStyle(sharedStyles);
    GM_addStyle(themeStyles);
    GM_addStyle(generalStyles);
    
    /*
     * Add dynamic styles
     */
    
    const dynamicStyles = `
      .plus-buttons {
        margin-right: -${buttons.getBoundingClientRect().width - 23}px;
      }

      .plus-buttons:hover {
        margin-right: 0;
      }
    `;
    
    GM_addStyle(dynamicStyles);
  });
})();