Story Downloader - Facebook and Instagram

Download stories (videos and images) from Facebook and Instagram.

目前為 2024-09-08 提交的版本,檢視 最新版本

// ==UserScript==
// @name         Story Downloader - Facebook and Instagram
// @namespace
// @version      1.2
// @description  Download stories (videos and images) from Facebook and Instagram.
// @author       oscar370
// @match        *.facebook.com/*
// @match        *.instagram.com/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=facebook.com
// @grant        none
// @license      GPL3
// @namespace https://gf.qytechs.cn/users/1364710
// ==/UserScript==

(function () {
  "use strict";

  const createDownloadButton = () => {
    // Initialization
    const button = document.createElement("button");
    const icon = document.createElementNS("http://www.w3.org/2000/svg", "svg");
    const path1 = document.createElementNS(
      "http://www.w3.org/2000/svg",
      "path"
    );
    const currentUrl = window.location.href;
    let topBar;

    // Determines the location of the upper div
    if (currentUrl.includes("facebook")) {
      topBar = document.querySelector(
        ".x78zum5.xds687c.xw3qccf.x10l6tqk.xtotuo0"
      );
    } else {
      const topBars = document.querySelectorAll(".x6s0dn4.x78zum5.x1xmf6yo");
      topBar = Array.from(topBars).find((bar) => bar.clientHeight > 0);
    }

    // Button properties
    button.id = "downloadButton";
    Object.assign(button.style, {
      cursor: "pointer",
      border: "none",
      backgroundColor: "transparent",
      zIndex: "9999",
    });

    // Icon properties
    icon.setAttribute("width", "24");
    icon.setAttribute("height", "24");
    icon.setAttribute("fill", "currentColor");
    icon.setAttribute("class", "bi bi-file-arrow-down-fill");
    icon.setAttribute("viewBox", "0 0 16 16");
    path1.setAttribute(
      "d",
      "M12 0H4a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2M8 5a.5.5 0 0 1 .5.5v3.793l1.146-1.147a.5.5 0 0 1 .708.708l-2 2a.5.5 0 0 1-.708 0l-2-2a.5.5 0 1 1 .708-.708L7.5 9.293V5.5A.5.5 0 0 1 8 5"
    );

    // Adding elements and events
    icon.appendChild(path1);
    button.appendChild(icon);
    topBar.appendChild(button);
    button.addEventListener("click", handleButtonClick);
  };

  const handleButtonClick = () => {
    // Initialization
    const dateStr = new Date().toISOString().split("T")[0];
    const currentUrl = window.location.href;
    const userName = currentUrl.includes("facebook")
      ? document.querySelector(
          ".x1i10hfl.x1qjc9v5.xjbqb8w.xjqpnuy.xa49m3k.xqeqjp1.x2hbi6w.x13fuv20.xu3j5b3.x1q0q8m5.x26u7qi.x972fbf.xcfux6l.x1qhh985.xm0m39n.x9f619.x1ypdohk.xdl72j9.x2lah0s.xe8uvvx.xdj266r.x11i5rnm.xat24cr.x1mh8g0r.x2lwn1j.xeuugli.xexx8yu.x4uap5.x18d9i69.xkhd6sd.x1n2onr6.x16tdsg8.x1hl2dhg.xggy1nq.x1ja2u2z.x1t137rt.x1o1ewxj.x3x9cwd.x1e5q0jg.x13rtm0m.x1q0g3np.x87ps6o.x1lku1pv.x1rg5ohu.x1a2a7pz.x193iq5w"
        ).innerText ?? "uknown"
      : document.querySelector(
          ".x1i10hfl.xjbqb8w.x1ejq31n.xd10rxx.x1sy0etr.x17r0tee.x972fbf.xcfux6l.x1qhh985.xm0m39n.x9f619.x1ypdohk.xt0psk2.xe8uvvx.xdj266r.x11i5rnm.xat24cr.x1mh8g0r.xexx8yu.x4uap5.x18d9i69.xkhd6sd.x16tdsg8.x1hl2dhg.xggy1nq.x1a2a7pz.xeuugli._a6hd"
        ).innerText ?? "uknown";
    const videos = document.querySelectorAll("video");
    let videoUrl = null;

    // Performs the video search
    for (const video of videos) {
      if (video.offsetHeight === 0) continue;

      let reactKey = "";
      const keys = Object.keys(video);

      for (const key of keys) {
        if (key.includes("__reactFiber")) {
          reactKey = key.split("__reactFiber")[1];
          break;
        }
      }
      videoUrl =
        video.parentElement.parentElement.parentElement.parentElement[
          `__reactProps${reactKey}`
        ]?.children[0]?.props?.children?.props?.implementations[1]?.data
          ?.hdSrc ||
        video.parentElement.parentElement.parentElement.parentElement[
          `__reactProps${reactKey}`
        ]?.children[0]?.props?.children?.props?.implementations[1]?.data
          ?.sdSrc ||
        video.parentElement.parentElement.parentElement.parentElement[
          `__reactProps${reactKey}`
        ]?.children?.props?.children?.props?.implementations[1]?.data?.hdSrc ||
        video.parentElement.parentElement.parentElement.parentElement[
          `__reactProps${reactKey}`
        ]?.children?.props?.children?.props?.implementations[1]?.data?.sdSrc ||
        video[`__reactFiber${reactKey}`]?.return?.stateNode?.props?.videoData
          ?.$1?.hd_src ||
        video[`__reactFiber${reactKey}`]?.return?.stateNode?.props?.videoData
          ?.$1?.sd_src;
      if (videoUrl) break;
    }

    const videoDownload = async () => {
      try {
        console.log("Descarga del vídeo en curso");
        const response = await fetch(videoUrl);
        const blob = await response.blob();
        const link = document.createElement("a");
        link.href = URL.createObjectURL(blob);
        link.download = `${userName}-${dateStr}.mp4`;
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
      } catch (error) {
        console.error("Error downloading video:", error);
      }
    };

    const imageDownload = async () => {
      let images;
      let image;

      if (currentUrl.includes("facebook")) {
        images = document.querySelectorAll(
          ".xz74otr.xjbqb8w.x3x9cwd.x1o1ewxj.x17qophe.x10l6tqk.xwa60dl.x1cb1t30.xh8yej3.x1ja2u2z"
        );
        image = Array.from(images).find((img) => img.clientHeight > 0).src;
      } else {
        images = document.querySelectorAll(
          ".xl1xv1r.x5yr21d.xmz0i5r.x193iq5w.xh8yej3"
        );
        image = Array.from(images).find((img) => img.clientHeight > 0).src;
      }

      try {
        const response = await fetch(image);
        const blob = await response.blob();
        const link = document.createElement("a");
        link.href = URL.createObjectURL(blob);
        link.download = `${userName}-${dateStr}.jpg`;
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
      } catch (error) {
        console.error("Error downloading image:", error);
      }
    };

    videoUrl ? videoDownload() : imageDownload();
  };

  const removeDownloadButton = () => {
    const button = document.querySelector("#downloadButton");
    button && document.body.removeChild(button);
  };

  // URL Monitoring
  let previousPath = window.location.pathname;

  const checkURL = () => {
    const currentPath = window.location.pathname;
    if (currentPath !== previousPath) {
      previousPath = currentPath;
      const visibleButton = document.querySelector("#downloadButton");
      if (currentPath.includes("/stories/")) {
        if (!visibleButton) {
          setTimeout(() => {
            createDownloadButton();
          }, 1000);
        }
      } else {
        if (visibleButton) {
          removeDownloadButton();
        }
      }
    }
  };

  checkURL();

  const observer = new MutationObserver(() => {
    checkURL();
  });

  observer.observe(document.body, { childList: true, subtree: true });
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址