Greasy Fork is available in English.

LightShot (prnt.sc) random screenshot

Press R on prnt.sc website to load some random screenshot

À partir de 2022-01-12. Voir la dernière version.

// ==UserScript==
// @name             LightShot (prnt.sc) random screenshot
// @description      Press R on prnt.sc website to load some random screenshot
//
// @name:ru          Lightshot (prnt.sc) случайный скриншот
// @description:ru   Нажми R на сайте prnt.sc чтобы загрузить какой-то случайный скриншот
//
// @author           Konf
// @namespace        https://greasyfork.org/users/424058
// @icon             https://www.google.com/s2/favicons?domain=prnt.sc&sz=32
// @version          1.2.0
// @match            https://prnt.sc/*
// @compatible       Chrome
// @compatible       Opera
// @compatible       Firefox
// @require          https://cdnjs.cloudflare.com/ajax/libs/arrive/2.4.1/arrive.min.js
// @run-at           document-body
// @grant            GM_addStyle
// @noframes
// ==/UserScript==

/* jshint esversion: 8 */

(function() {
  'use strict';

  const langStrings = {
    en: {
      getNewRandImg: 'Load new random screenshot',
      hotkey: 'Hotkey',
      scriptGotFailStreak:
        'Oops! Script just have got a huge fail streak. ' +
        'If your internet connection is fine, maybe the script ' +
        'is broken. Please consider to notify the script author ' +
        'about the problem. Also it would be great if you check ' +
        'your browser console and save all the info messages ' +
        'from there. They are contain the errors details',
    },
    ru: {
      getNewRandImg: 'Загрузить новый случайный скриншот',
      hotkey: 'Горячая клавиша',
      scriptGotFailStreak:
        'Упс! Скрипт много пытался, но так и не смог ' +
        'сработать. Если у тебя всё в порядке с интернетом, ' +
        'то возможно скрипт просто сломан. Пожалуйста, сообщи ' +
        'о проблеме автору скрипта. А ещё было бы супер если ' +
        'бы ты открыл(а) консоль браузера и сохранил(а) оттуда ' +
        'все сообщения. Они содержат описания ошибок',
    },
  };
  const userLang = navigator.language.slice(0, 2);
  const i18n = langStrings[userLang || 'en'];

  const css = [`
    body {
      overflow-y: scroll;
    }

    .parser-icon {
      float: left;
      width: 28px;
      height: 28px;
      margin: 11px 25px 0 0;
      color: white;
      font-weight: bold;
      user-select: none;
      cursor: pointer;
      border: 2px solid lightgray;
      border-radius: 100%;
      outline: none;
      background: none;
    }

    .parser-icon--loading {
      /* hide text */
      text-indent: -9999em;
      white-space: nowrap;
      overflow: hidden;

      border-top: 5px solid rgba(255, 255, 255, 0.2);
      border-right: 5px solid rgba(255, 255, 255, 0.2);
      border-bottom: 5px solid rgba(255, 255, 255, 0.2);
      border-left: 5px solid #ffffff;
      transform: translateZ(0);
      animation: loading 1.1s infinite linear;
    }

    @keyframes loading {
      0% {
        transform: rotate(0deg);
      }
      100% {
        transform: rotate(360deg);
      }
    }
  `].join();

  // node queries
  const qs = {
    garbage: [
      'div.image__title.image-info-item', 'div.additional',
      'div.header-downloads', 'div.social', 'div.image-info',
    ].join(', '),

    needed: {
      mainImg: {
        q: 'img.no-click.screenshot-image',
        node: null,
      },
      headerLogo: {
        q: 'a.header-logo',
        node: null,
      },
      headerLogoParent: {
        q: 'div.header > div.page-constrain',
        node: null,
      },
    }
  };

  document.arrive(qs.garbage, { existing: true }, n => n.remove());

  let injected = false;

  for (const nodeId in qs.needed) {
    const nodeObj = qs.needed[nodeId];

    // eslint-disable-next-line no-loop-func
    document.arrive(nodeObj.q, { existing: true }, (aNode) => {
      nodeObj.node = aNode;

      const neededNodesAmount = Object.keys(qs.needed).length;
      let arrivedCount = 0;

      for (const nodeId in qs.needed) {
        const nodeObj = qs.needed[nodeId];

        if (nodeObj.node) arrivedCount += 1;
      }

      if (
        injected === false &&
        arrivedCount === neededNodesAmount
      ) {
        injected = true;
        main();
      }
    });
  }


  function main() {
    GM_addStyle(css);

    const b = document.createElement('button'); // fetch button
    const bParent = qs.needed.headerLogoParent.node;
    const bNeighbour = qs.needed.headerLogo.node;

    b.innerText = 'R';
    b.title = `${i18n.getNewRandImg}\n${i18n.hotkey}: R`;
    b.className = 'parser-icon';
    bParent.insertBefore(b, bNeighbour);

    b.addEventListener('click', loadNewImg);
    document.addEventListener('keydown', ev => {
      if (ev.code === 'KeyR') loadNewImg();
    });

    let failsStreak = 0;
    let isFetching = false;

    function closeFetchUX() {
      isFetching = false;
      b.className = 'parser-icon';
    }

    async function loadNewImg() {
      if (isFetching) return;

      isFetching = true;
      b.className = 'parser-icon parser-icon--loading';

      const newSshotUrl = `https://prnt.sc/${makeImgId()}`;

      try {
        const newSshotPage = await fetch(newSshotUrl);
        const tempDiv = document.createElement('div');

        tempDiv.innerHTML = await newSshotPage.text();

        const mainImg = qs.needed.mainImg.node;
        const fetchedImgNode = tempDiv.querySelector(qs.needed.mainImg.q);

        if (!fetchedImgNode || !fetchedImgNode.src) {
          throw new Error(
            'Failed to find a new image in fetched webpage. ' +
            'URL: ' + newSshotUrl
          );
        }

        let newSshotBlob;

        try {
          const newSshotImg = await fetch(fetchedImgNode.src);

          newSshotBlob = await newSshotImg.blob();
        } catch (e) {
          console.error(e);

          throw new Error(
            `Failed to load ${fetchedImgNode.src} ` +
            `that was fetched from ${newSshotUrl}`
          );
        }

        mainImg.src = URL.createObjectURL(newSshotBlob);

        closeFetchUX();
        history.pushState(null, null, newSshotUrl);
        failsStreak = 0;
      } catch (e) {
        failsStreak += 1;
        console.error(e);
        closeFetchUX();

        if (failsStreak < 20) {
          // retry immediately (almost)
          setTimeout(loadNewImg, 250);
        } else {
          alert(`${GM_info.script.name}:\n${i18n.scriptGotFailStreak}`);
          failsStreak = 0;
        }
      }
    }

    window.addEventListener('popstate', reloadPage);
  }


  // utils ---------------------------------------------

  function reloadPage() {
    window.location.reload();
  }

  function makeImgId() {
    const chars = {
      first: 'abcdefghijklmnopqrstuvwxyz123456789',
      rest: 'abcdefghijklmnopqrstuvwxyz0123456789',
    };

    return makeId(chars.first, 1) + makeId(chars.rest, 5);
  }

  function makeId(charset, length) {
    let result = '';

    for (let i = 0, randNum; i < length; i++) {
      randNum = Math.floor(Math.random() * charset.length);
      result += charset.charAt(randNum);
    }

    return result;
  }

  // ---------------------------------------------------
})();