Direct Picture Link (Instagram)

Direct image links for sites that obfuscate the image from easy downloading.

目前為 2021-12-08 提交的版本,檢視 最新版本

// ==UserScript==
// @name         Direct Picture Link (Instagram)
// @namespace    https://lawrenzo.com/p/direct-picture-link
// @version      0.4
// @description  Direct image links for sites that obfuscate the image from easy downloading.
// @author       Lawrence Sim
// @license      WTFPL (http://www.wtfpl.net)
// @match        https://*.instagram.com/*
// @grant        none
// ==/UserScript==
(function() {
    var contextMenu;
    var openContextMenu = (evt, src, type) => {
        let a;
        if(!contextMenu) {
            contextMenu = document.createElement("div");
            contextMenu.style["position"] = "absolute";
            contextMenu.style["background"] = "#efefef";
            contextMenu.style["border-radius"] = "0.2em";
            contextMenu.style["box-shadow"] = "1px 1px 3px 1px rgba(0,0,0,0.4)";
            contextMenu.style["padding"] = "0.2em 0.5em";
            contextMenu.style["font-size"] = "0.85em";
            contextMenu.style["z-index"] = "999999";
            a = document.createElement("a");
            a.innerHTML = "> view direct source";
            a.setAttribute("target", "_blank");
            a.setAttribute("href", src);
            contextMenu.append(a);
            document.body.append(contextMenu);
        } else {
            a = contextMenu.querySelector("a");
            a.setAttribute("src", src);
        }
        if(type === "video") {
            a.innerHTML = "> view direct video";
        } else {
            a.innerHTML = "> view direct image";
        }
        contextMenu.style.left = `${evt.pageX}px`;
        contextMenu.style.top = `${evt.pageY}px`;
        if(type === "video") {
            a.addEventListener('click', evt => {
                evt.preventDefault();
                let xhr = new XMLHttpRequest();
                xhr.responseType = 'blob';
                xhr.onload = () => {
                    let recoveredBlob = xhr.response,
                        reader = new FileReader();
                    reader.onload = () => window.open(reader.result);
                    reader.readAsDataURL(recoveredBlob);
                };
                xhr.open('GET', src);
                xhr.send();
            });
        }
    };
    document.body.addEventListener("click", () => {
        if(!contextMenu) return;
        contextMenu.remove();
        contextMenu = null;
    });
    var link = (mutated, observer) => {
        mutated = mutated || [{target: document.body}];
        mutated.forEach(mutant => {
            mutant.target.querySelectorAll("article img").forEach(img => {
                if(img.getAttribute("ilnkd")) return;
                let multi = img.closest("li > div"),
                    btn = multi ? multi.querySelector("div[role='button']") : img.closest("div[role='button']");
                if(!btn || multi && img.parentNode.firstChild.tagName === "VIDEO") return;
                btn.addEventListener("contextmenu", (evt) => {
                    evt.preventDefault();
                    openContextMenu(evt, img.getAttribute("src"));
                });
                img.setAttribute("ilnkd", 1);
            });
            mutant.target.querySelectorAll("article video").forEach(vid => {
                if(vid.getAttribute("ilnkd")) return;
                let multi = vid.closest("li > div"),
                    btn = multi ? multi.firstChild : vid.parentNode.parentNode.parentNode.parentNode;
                if(!btn) return;
                btn.addEventListener("contextmenu", (evt) => {
                    evt.preventDefault();
                    openContextMenu(evt, vid.getAttribute("src"), "video");
                });
                vid.setAttribute("ilnkd", 1);
            });
        });
    };
    link();
    var obs = new MutationObserver(link);
    obs.observe(document.body, {childList:true, subtree:true});
})();