Raw Source
Apkawa / Pikabu download video helper

// ==UserScript==
// @name         Pikabu download video helper
// @namespace    http://tampermonkey.net/
// @version      0.4
// @description  Helpers for display direct url for video
// @author       Apkawa
// @license      MIT
// @icon         https://www.google.com/s2/favicons?domain=pikabu.ru
// @match        https://pikabu.ru/*
// @homepage     https://github.com/Apkawa/userscripts
// @homepageURL  https://github.com/Apkawa/userscripts
// @supportURL   https://github.com/Apkawa/userscripts/issues
// @downloadURL  https://github.com/Apkawa/userscripts/raw/master/dist/video_url.user.js
// @updateURL    https://github.com/Apkawa/userscripts/raw/master/dist/video_url.user.js
// ==/UserScript==
// src/utils/dom.ts
function waitElement(match, callback, root = document.body) {
  const observer = new MutationObserver((mutations) => {
    let matchFlag = false;
    mutations.forEach((mutation) => {
      if (!mutation.addedNodes)
        return;
      for (let i = 0;i < mutation.addedNodes.length; i++) {
        const node = mutation.addedNodes[i];
        matchFlag = match(node);
      }
    });
    if (matchFlag) {
      _stop();
      callback();
      _start();
    }
  });
  let isStarted = false;
  function _start() {
    if (isStarted) {
      return;
    }
    observer.observe(root || document.body, {
      childList: true,
      subtree: true,
      attributes: false,
      characterData: false
    });
    isStarted = true;
  }
  function _stop() {
    observer.disconnect();
    isStarted = false;
  }
  _start();
  return () => {
    _stop();
  };
}
// node_modules/rxjs/_esm5/internal/util/isFunction.js
function isFunction(x) {
  return typeof x === "function";
}
// src/utils/location.ts
function matchLocation(...patterns) {
  const s = document.location.href;
  for (const p of patterns) {
    if (isFunction(p) && p(s)) {
      return true;
    }
    if (RegExp(p).test(s)) {
      return true;
    }
  }
  return false;
}
// src/utils/index.ts
var entries = Object.entries;

// src/pikabu.ru/video_url.user.ts
(() => {
  if (!matchLocation("^https://pikabu.ru/.*")) {
    return;
  }
  console.log("PIKABU Video url");
  const FLAG_ATTRIBUTE_NAME = "video_url_user";
  function extractSources(el) {
    const m = {};
    if (el.getAttribute("data-type") == "gifx") {
      const bg = el.querySelector('[class^="pkb-player-block__preview"]');
      if (bg) {
        const url = Array.from(bg.getAttribute("style")?.matchAll(/url\((['"])?(.*?)\1\)/gi) || [])[0][2];
        if (url) {
          const source = url.replace(/_p\.\w{3,4}$/, "");
          m["gif"] = `${source}.gif`;
        }
      }
    } else {
      const sources = el.querySelectorAll("source");
      for (const s of sources) {
        const url = s.getAttribute("src");
        if (url) {
          const parts = url?.split(/.(\w{3,4})$/);
          if (parts?.length > 1) {
            m[parts[1]] = url;
          }
        }
      }
    }
    return m;
  }
  function addDownloadButtonsForVideo(el) {
    if (el.getAttribute(FLAG_ATTRIBUTE_NAME)) {
      return;
    }
    const sources = entries(extractSources(el));
    if (!sources.length) {
      return;
    }
    const container = document.createElement("div");
    container.setAttribute("style", `display: flex; width: 100%; height: 25px; 
      align-items: center; justify-content: flex-start`);
    let html = "";
    for (const [ext, url] of sources) {
      const source = url.replace(/\.\w{3,4}$/, "");
      const name = source.split("/").pop();
      html += `<a 
        href='${url}' 
        style='padding: 5px; margin-right: 5px; 
        border: gray 1px solid; border-radius: 3px; height: 20px'
        download='${name || "download"}.${ext}'
        target='_blank'
        >${ext}</a>`;
    }
    container.innerHTML = html;
    el.parentNode && el.parentNode.insertBefore(container, el.nextSibling);
    el.setAttribute(FLAG_ATTRIBUTE_NAME, "1");
  }
  waitElement((el) => {
    const _el = el;
    return Boolean(_el.querySelectorAll && _el.querySelectorAll('[data-role="player"] [class^="pkb-player-block__preview"]'));
  }, () => {
    document.querySelectorAll && document.querySelectorAll('[data-role="player"]').forEach((el) => addDownloadButtonsForVideo(el));
  });
})();