Guest User

[Tampermonkey] Danbooru Enhanced Gallery View

a guest
May 28th, 2023
104
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. // ==UserScript==
  2. // @name         Danbooru Enhanced Gallery View
  3. // @namespace    http://tampermonkey.net/
  4. // @version      1.0
  5. // @description  Adds functionality to the image gallery like media preview and download without the need to open the post.
  6. // @author       SomeHornyDev
  7. // @match        https://danbooru.donmai.us/*
  8. // @exclude      https://danbooru.donmai.us/posts/*
  9. // @icon         https://www.google.com/s2/favicons?sz=64&domain=donmai.us
  10. // @grant        none
  11. // ==/UserScript==
  12.  
  13. const MEDIA_VIEWER_ELEM_CLASS = 'media-viewer-element';
  14. const MEDIA_VIEWER_DOWNLOAD_BUTTON_CLASS = 'media-viewer-download';
  15. const MEDIA_VIEWER_DOWNLOAD_BUTTON_POST_URL_DATA_ATTR = 'data-post-url';
  16.  
  17. class MediaViewer {
  18.     #container;
  19.     #content;
  20.     #downloadButton;
  21.     #isOpen;
  22.  
  23.     constructor() {
  24.         this.#isOpen = false;
  25.  
  26.         this.#container = document.createElement('div');
  27.         Object.assign(this.#container.style, {
  28.             isolation: 'isolate',
  29.             position: 'fixed',
  30.             inset: 0,
  31.             display: 'none',
  32.             flexDirection: 'column',
  33.             alignItems: 'center',
  34.             justifyContent: 'center',
  35.             padding: '48px',
  36.             backgroundColor: 'rgba(0, 0, 0, 0.5)',
  37.             pointerEvents: 'none'
  38.         });
  39.  
  40.         this.#container.addEventListener('click', (evt) => {
  41.             const elem = evt.target;
  42.             if (this.#isOpen && !elem.classList.contains(MEDIA_VIEWER_ELEM_CLASS) && !elem.classList.contains(MEDIA_VIEWER_DOWNLOAD_BUTTON_CLASS)) {
  43.                 this.close();
  44.             }
  45.         });
  46.  
  47.         document.addEventListener('keyup', (evt) => {
  48.             if (evt.key === "Escape") {
  49.                 if (this.#isOpen) this.close();
  50.             }
  51.         });
  52.  
  53.         this.#content = document.createElement('div');
  54.         this.#container.append(this.#content);
  55.  
  56.         this.#downloadButton = document.createElement('button');
  57.         this.#downloadButton.textContent = '⬇️ Download';
  58.         Object.assign(this.#downloadButton.style, {
  59.             display: 'inline-flex',
  60.             alignItems: 'center',
  61.             justifyContent: 'center',
  62.             gap: '2px',
  63.             marginTop: '8px',
  64.             padding: '12px',
  65.             backgroundColor: 'white',
  66.             color: 'black',
  67.             fontSize: '1rem',
  68.             border: '0',
  69.             borderRadius: '4px',
  70.             boxShadow: 'none',
  71.         });
  72.         this.#downloadButton.classList.add(MEDIA_VIEWER_DOWNLOAD_BUTTON_CLASS);
  73.         this.#downloadButton.addEventListener('click', (evt) => {
  74.             const postUrl = evt.target.getAttribute(MEDIA_VIEWER_DOWNLOAD_BUTTON_POST_URL_DATA_ATTR);
  75.             downloadMediaFromPostUrl(postUrl);
  76.         });
  77.  
  78.         this.#container.append(this.#downloadButton);
  79.  
  80.  
  81.         document.body.append(this.#container);
  82.     }
  83.  
  84.     open(url) {
  85.         this.#content.innerHTML = 'Loading...';
  86.  
  87.         getMediaElemFromPostUrl(url)
  88.             .then((elem) => {
  89.             this.#content.innerHTML = '';
  90.  
  91.             Object.assign(elem.style, {
  92.                 display: 'block',
  93.                 maxInlineSize: '100%',
  94.                 blockSize: 'auto',
  95.                 maxHeight: 'calc(100vh - 96px)',
  96.                 border: '4px solid white',
  97.                 borderRadius: '4px'
  98.             });
  99.  
  100.             elem.classList.add(MEDIA_VIEWER_ELEM_CLASS);
  101.  
  102.             this.#content.append(elem);
  103.  
  104.             this.#downloadButton.setAttribute(MEDIA_VIEWER_DOWNLOAD_BUTTON_POST_URL_DATA_ATTR, url);
  105.  
  106.             Object.assign(this.#container.style, {
  107.                 display: 'flex',
  108.                 pointerEvents: 'auto'
  109.             });
  110.  
  111.             this.#isOpen = true;
  112.         });
  113.     }
  114.  
  115.     close() {
  116.         Object.assign(this.#container.style, {
  117.             display: 'none',
  118.             pointerEvents: 'none'
  119.         });
  120.  
  121.         this.#isOpen = false;
  122.     }
  123. }
  124.  
  125. function getOrigSrcFromPostUrl(postUrl) {
  126.     return fetch(postUrl)
  127.         .then((response) => response.text())
  128.         .then((html) => {
  129.         const parser = new DOMParser();
  130.         const doc = parser.parseFromString(html, 'text/html');
  131.  
  132.         const videoElem = doc.querySelector('video');
  133.         if (videoElem) {
  134.             return videoElem.src;
  135.         }
  136.  
  137.         const viewOriginalLink = doc.querySelector('a.image-view-original-link');
  138.         return viewOriginalLink.href;
  139.     })
  140.         .catch((err) => console.error(err));
  141. }
  142.  
  143. function getMediaElemFromPostUrl(postUrl) {
  144.     return fetch(postUrl)
  145.         .then((response) => response.text())
  146.         .then((html) => {
  147.         const parser = new DOMParser();
  148.         const doc = parser.parseFromString(html, 'text/html');
  149.  
  150.         const videoElem = doc.querySelector('video');
  151.         if (videoElem) {
  152.             videoElem.muted = true;
  153.             return videoElem;
  154.         }
  155.  
  156.         const img = new Image();
  157.  
  158.         const mainImage = doc.getElementById('image');
  159.         const viewOriginalLink = doc.querySelector('a.image-view-original-link');
  160.  
  161.         if (viewOriginalLink) {
  162.             img.src = viewOriginalLink.href;
  163.         } else {
  164.             img.src = mainImage.src;
  165.         }
  166.  
  167.         return img;
  168.     })
  169.         .catch((err) => console.error(err));
  170. }
  171.  
  172. function getBlobFromUrl(url) {
  173.     return fetch(url)
  174.         .then((response) => response.blob())
  175.         .catch((err) => console.error(err));
  176. }
  177.  
  178. function downloadMediaFromPostUrl(postUrl) {
  179.     getOrigSrcFromPostUrl(postUrl)
  180.         .then((src) => {
  181.         getBlobFromUrl(src)
  182.             .then((blob) => {
  183.             const anchor = document.createElement("a");
  184.             anchor.href = URL.createObjectURL(blob);
  185.  
  186.             const fileName = src.match(/[^\/]+$/);
  187.             anchor.download = fileName;
  188.  
  189.             document.body.appendChild(anchor);
  190.             anchor.click();
  191.  
  192.             document.body.removeChild(anchor);
  193.             URL.revokeObjectURL(blob);
  194.         });
  195.     });
  196. }
  197.  
  198. (function() {
  199.     'use strict';
  200.  
  201.     const mediaViewer = new MediaViewer();
  202.  
  203.     const postGallery = document.querySelector('.post-gallery');
  204.     const imagePreviews = postGallery.querySelectorAll('.post-preview');
  205.  
  206.     imagePreviews.forEach((imagePreview) => {
  207.         const link = imagePreview.querySelector('.post-preview-link');
  208.         const source = imagePreview.querySelector('source');
  209.         const postUrl = link.href;
  210.  
  211.         Object.assign(imagePreview.style, {
  212.             position: 'relative',
  213.             padding: '32px',
  214.             backgroundColor: 'rgba(255, 255, 255, 0.03)',
  215.             borderRadius: '2px',
  216.             boxShadow: '0px 3px 15px rgba(0,0,0,0.2)'
  217.         });
  218.  
  219.         const buttonGroup = document.createElement('div');
  220.         Object.assign(buttonGroup.style, {
  221.             position: 'absolute',
  222.             top: '-6px',
  223.             right: '-6px',
  224.             display: 'flex',
  225.             alignItems: 'center',
  226.             visibility: 'hidden',
  227.         });
  228.  
  229.         imagePreview.append(buttonGroup);
  230.  
  231.         imagePreview.addEventListener('mouseenter', () => {
  232.             imagePreview.style.backgroundColor = 'rgba(255, 255, 255, 0.1)';
  233.             buttonGroup.style.visibility = 'visible';
  234.         });
  235.  
  236.         imagePreview.addEventListener('mouseleave', () => {
  237.             imagePreview.style.backgroundColor = 'rgba(255, 255, 255, 0.03)';
  238.             buttonGroup.style.visibility = 'hidden';
  239.         });
  240.  
  241.         const buttonStyles = {
  242.             display: 'flex',
  243.             alignItems: 'center',
  244.             justifyContent: 'center',
  245.             alignSelf: 'end',
  246.             padding: '8px',
  247.             backgroundColor: 'transparent',
  248.             lineHeight: '1.25',
  249.             fontSize: '1.25rem',
  250.             border: '0',
  251.             boxShadow: 'none'
  252.         };
  253.  
  254.         const previewButton = document.createElement('button');
  255.         previewButton.textContent = '🔎';
  256.         Object.assign(previewButton.style, buttonStyles);
  257.         previewButton.addEventListener('click', () => mediaViewer.open(postUrl));
  258.  
  259.         const downloadButton = document.createElement('button');
  260.         downloadButton.textContent = '⬇️';
  261.         Object.assign(downloadButton.style, buttonStyles);
  262.         downloadButton.addEventListener('click', () => downloadMediaFromPostUrl(postUrl));
  263.  
  264.         buttonGroup.append(previewButton);
  265.         buttonGroup.append(downloadButton);
  266.     });
  267. })();
Add Comment
Please, Sign In to add comment