Raw Source
nljuggler / Instagram Story Saver

// ==UserScript==
// @name         Instagram Story Saver
// @version      2.6
// @description  Click button to open the IG story video/image in new tab. Then you can View/save/download the story.
// @author       navchandar
// @copyright    2019, navchandar (https://openuserjs.org/users/navchandar)
// @license      MIT
// @match        https://www.instagram.com/*
// @match        https://www.instagram.com/stories/*
// excludes      *
// @grant        GM_openInTab
// @grant        GM_addStyle
// @homepage     https://navchandar.github.io/
// @homepage     https://github.com/navchandar/
// @homepageURL  https://navchandar.github.io/
// @updateURL    https://openuserjs.org/meta/navchandar/Instagram_Story_Saver.meta.js
// @downloadURL  https://openuserjs.org/install/navchandar/Instagram_Story_Saver.user.js
// @supportURL   https://openuserjs.org/scripts/navchandar/Instagram_Story_Saver/issues
// @setupURL     https://openuserjs.org/install/navchandar/Instagram_Story_Saver.user.js
// @icon         https://cdn.iconscout.com/icon/free/png-256/instagram-53-151118.png
// ==/UserScript==

function getElementsByXPath(xpath, parent) {
  let results = [];
  let query = document.evaluate(xpath, parent || document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
  for (let i = 0, length = query.snapshotLength; i < length; ++i) {
    results.push(query.snapshotItem(i))
  }
  return results
}

function has(String, search) {
  try {
    if (String.indexOf(search) > -1) {
      return true;
    }
  }
  catch (err) {}
  return false;
}

function openStory(zEvent) {
  let parent = document.elementFromPoint(innerWidth / 2, innerHeight / 2).parentNode.parentNode.parentNode.parentNode,
    video = parent.querySelector("video source"),
    image = parent.querySelector("img");

  if (!video && !image) {
    parent = document.elementFromPoint(innerWidth / 2, innerHeight / 2).parentNode.parentNode.parentNode.parentNode.parentNode.parentNode;
    video = parent.querySelector("video source");
    image = parent.querySelector("img");
  }

  if (!video && !image) {
    parent = document.elementFromPoint(innerWidth / 2, innerHeight / 2).parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode;
    video = parent.querySelector("video source");
    image = parent.querySelector("img");
  }

  if (video) {
    GM_openInTab(video.getAttribute("src"), false);
  }
  else if (image) {
    GM_openInTab(GetFirstSrcsetUrl(image.getAttribute("srcset")), false);
  }
  else {
    console.log("Nothing img/video found to open.")
  }
}

function GetFirstSrcsetUrl(imageSrc)
{
    var firstUrl = /(^http.*?) /g.exec(imageSrc)[0];
    console.log(firstUrl);
    return firstUrl;
}
function AddButton() {
  // Add a button element on div
  var zNode = document.createElement('div');
  zNode.innerHTML = '<button id="myButton" title="Click to open this Story" type="button">OPEN</button>';
  zNode.setAttribute('id', 'myContainer');
  document.body.appendChild(zNode);

  //--- Activate the newly added button.
  document.getElementById("myButton").addEventListener("click", openStory, false);

  //--- Style our newly added element using CSS.
  GM_addStyle(`#myContainer{position:fixed;bottom:0;left:49%;right:48%;margin:0;width:3.5%;height:3%;text-align:center}
#myButton{opacity:.55;cursor:pointer;background-color:#262626;color:#fff;font-size:7px;border-radius:7px;padding:0}#myButton:hover{color:#fff;opacity:.65}`);

}

function sleep(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

async function sleepFunc() {
  await sleep(1000);
}

function Login() {
  // Auto login if creds autofilled
  setTimeout(function () {
    sleepFunc();
    var login = getElementsByXPath("//*[text() = 'Log In']");
    if (login.length > 0) {
      login[0].click();
    }
    var notNow = getElementsByXPath("(//button[text()='Not Now'])[1]");
    if (notNow.length > 0) {
      notNow[0].click();
    }
  }, 2000)

  // click the link NOT NOW after login!!
  var NotNow = getElementsByXPath("//a[@href='/']");
  if (NotNow.length > 0) {
    NotNow[0].click();
  }
}

function HideBtn() {
  document.getElementById('myButton').style.visibility = 'hidden';
}

function UnHideBtn() {
  document.getElementById('myButton').style.visibility = 'visible';
}

function AddShortcut() {
  document.addEventListener('keydown', keyHandler);

  function keyHandler(event) {
    if (window.location.href === 'https://www.instagram.com/') return;
    // alt + x
    if (event.altKey && event.keyCode === 88) {
      document.getElementById("myButton").click();
    }
  }

}

(function () {
  'use strict';
  AddButton();
  HideBtn();
  Login();
  AddShortcut();

  // call this every 2 seconds to update according to page load
  setInterval(function () {
    var hostURL = window.location.href;
    if (has(hostURL, 'stories') && has(hostURL, 'instagram')) {
      UnHideBtn();
    }
    else {
      HideBtn();
    }
  }, 1000);

})();