NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==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/pikabu.ru/video_url.user.js
// @updateURL https://github.com/Apkawa/userscripts/raw/master/dist/pikabu.ru/video_url.user.js
// ==/UserScript==
(() => {
"use strict";
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();
};
}
function isFunction(x) {
return typeof x === "function";
}
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;
}
Object.keys;
const entries = Object.entries;
Object.values;
(function() {
"use strict";
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; \n 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 \n href='${url}' \n style='padding: 5px; margin-right: 5px; \n border: gray 1px solid; border-radius: 3px; height: 20px'\n download='${name || "download"}.${ext}'\n target='_blank'\n >${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));
});
})();
})();