|
| 1 | +export default { |
| 2 | + icon: "https://www.google.com/s2/favicons?domain=instagram.com", |
| 3 | + name: { |
| 4 | + en: "Instagram photos/videos downloader", |
| 5 | + vi: "Instagram - tải video / hình ảnh", |
| 6 | + }, |
| 7 | + description: { |
| 8 | + en: "Download ", |
| 9 | + vi: "", |
| 10 | + }, |
| 11 | + blackList: [], |
| 12 | + whiteList: ["*://*.instagram.com/*"], |
| 13 | + |
| 14 | + func: function () { |
| 15 | + // Original source code: https://greasyfork.org/en/scripts/14755-instagram-reloaded |
| 16 | + // Modified by Hoang Tran |
| 17 | + |
| 18 | + // ==UserScript== |
| 19 | + // @name Instagram Reloaded |
| 20 | + // @namespace http://despecial.de |
| 21 | + // @homepageURL https://greasyfork.org/en/scripts/14755-instagram-reloaded |
| 22 | + // @version 2.30 |
| 23 | + // @description View or download the full-size Instagram image/video. Super simple: press alt+f or shift & click to view media - alt & click saves file. Read for more options. |
| 24 | + // @author despecial |
| 25 | + // @match *://*.instagram.com/* |
| 26 | + // @icon https://www.google.com/s2/favicons?domain=instagram.com |
| 27 | + // @require https://ajax.googleapis.com/ajax/libs/jquery/2.2.4/jquery.min.js |
| 28 | + // @grant GM_download |
| 29 | + // @grant GM_xmlhttpRequest |
| 30 | + // ==/UserScript== |
| 31 | + |
| 32 | + var ig = |
| 33 | + '#react-root section main article header ~ div._97aPb > div[role="button"] .FFVAD'; // single photo |
| 34 | + ig += ", #react-root section main article .tWeCl"; // single video |
| 35 | + ig += ', div[role="dialog"] article ._97aPb > div[role="button"] .FFVAD'; // popup photo |
| 36 | + ig += ', div[role="dialog"] article .tWeCl'; // popup video |
| 37 | + ig += ", #react-root section main article .vi798"; // new carousel photo |
| 38 | + ig += ', div[role="dialog"] .vi798'; // new popup carousel photo |
| 39 | + // ig+= ', #react-root section main article video'; // igtv video |
| 40 | + // ig+= ', div[role="dialog"] article header + div > div div div div video'; // popup igtv video |
| 41 | + ig += ",.EmbedFrame.EmbedMedia"; |
| 42 | + var alt_trigger = ig; |
| 43 | + alt_trigger += |
| 44 | + ', #react-root section main article[role="presentation"] div[aria-label="Control"]'; // click fix for videos |
| 45 | + alt_trigger += |
| 46 | + ', div[role="dialog"] article[role="presentation"] div[aria-label="Control"]'; // click fix for popup video |
| 47 | + |
| 48 | + (function ($) { |
| 49 | + function despecial_ig(e, $this, a) { |
| 50 | + if (!e) e = window.event; |
| 51 | + if ((e.shiftKey && e.altKey) || a == "rm") { |
| 52 | + var p, v, vf, bloburl; |
| 53 | + e.preventDefault(); |
| 54 | + |
| 55 | + // carousel photos |
| 56 | + if ($(".vi798 .Ckrof img.FFVAD").length) { |
| 57 | + var curr = getCurrentItem(); |
| 58 | + var carouselImages = $(".Ckrof img.FFVAD"); |
| 59 | + var currpos = curr == $(".Yi5aA").length ? 2 : 1; |
| 60 | + p = |
| 61 | + curr + 1 <= 2 |
| 62 | + ? getBestImage(carouselImages[curr]) |
| 63 | + : getBestImage(carouselImages[currpos]); |
| 64 | + } |
| 65 | + // get single photo |
| 66 | + else { |
| 67 | + if ($this.is("img")) { |
| 68 | + p = getBestImage($this); |
| 69 | + } |
| 70 | + } |
| 71 | + |
| 72 | + var parent = $this.parent(); |
| 73 | + if ( |
| 74 | + $('div[role="dialog"] article header + div').length && |
| 75 | + a == "rm" |
| 76 | + ) { |
| 77 | + if (parent.find("video + img").length) return; // only visible video |
| 78 | + v = parent.find("video").attr("videourl"); |
| 79 | + if (!v) v = parent.find("video").attr("src"); |
| 80 | + if (!v) vf = parent.find("video"); |
| 81 | + if (!p) p = parent.find("img").last().attr("src"); |
| 82 | + } else { |
| 83 | + if (parent.find("video + img").length) return; // only visible video |
| 84 | + v = parent.find("video").attr("videourl"); |
| 85 | + if (!v) v = parent.find("video").attr("src"); |
| 86 | + if (!v) v = $("meta[property='og:video']").attr("content"); |
| 87 | + if (!v) vf = $this.closest("div").find("video"); |
| 88 | + if (!v && !vf.attr("poster") && !p) |
| 89 | + p = $this.siblings().find("img").attr("src"); |
| 90 | + } |
| 91 | + var ep = $this.find(".efImage").css("background-image"), |
| 92 | + rplcd = new Array("XXXXXX"), |
| 93 | + t = e.altKey ? "_self" : "_blank", |
| 94 | + fs = |
| 95 | + typeof ep === "string" || ep instanceof String |
| 96 | + ? ep.replace(/^url\(['"]?([^'"]+)['"]?\)/, "$1") |
| 97 | + : p; |
| 98 | + for (var i = 0; i < rplcd.length; ++i) { |
| 99 | + var r = new RegExp(rplcd[i], "ig"); |
| 100 | + if (r.test(fs)) fs = fs.replace(r, ""); |
| 101 | + } |
| 102 | + var isChrome = !!window.chrome && !!window.chrome.webstore; |
| 103 | + if (isChrome && e.altKey) { |
| 104 | + if (fs) direct_download(fs); |
| 105 | + if (vf) video_download(vf); |
| 106 | + console.log("Chrome"); |
| 107 | + } else { |
| 108 | + if (v) { |
| 109 | + if (!v.startsWith("blob")) { |
| 110 | + window.open(v, t); |
| 111 | + window.setTimeout(stopVideo, 100); |
| 112 | + console.log("Video opening"); |
| 113 | + } else { |
| 114 | + bloburl = fetchVideoURL( |
| 115 | + window.location.href, |
| 116 | + $this.parent().find("video")[0], |
| 117 | + t |
| 118 | + ); |
| 119 | + console.log("Blob video loading"); |
| 120 | + } |
| 121 | + } |
| 122 | + if (!v && vf.isArray) { |
| 123 | + if (!vf[0].getAttribute("src").startsWith("blob")) { |
| 124 | + video_download(vf); |
| 125 | + console.log("Video2 opening"); |
| 126 | + } else { |
| 127 | + bloburl = fetchVideoURL( |
| 128 | + window.location.href, |
| 129 | + $this.parent().find("video")[0], |
| 130 | + t |
| 131 | + ); |
| 132 | + console.log("Blob2 video loading"); |
| 133 | + } |
| 134 | + } |
| 135 | + if (fs && !v && !bloburl) { |
| 136 | + window.open(fs, t); |
| 137 | + console.log("FS"); |
| 138 | + } |
| 139 | + } |
| 140 | + } |
| 141 | + } |
| 142 | + |
| 143 | + /* dynamic download link */ |
| 144 | + function direct_download(url) { |
| 145 | + var filename = url.match("[^/]*$")[0]; |
| 146 | + var arg = { url: url, name: filename }; |
| 147 | + GM_download(arg); |
| 148 | + } |
| 149 | + |
| 150 | + var csrf_token = $("body") |
| 151 | + .html() |
| 152 | + .match(/\"csrf_token\":(?:"[^"]*"|^[^"]*$)/)[0] |
| 153 | + .replace(/\"/g, "") |
| 154 | + .split(":")[1]; |
| 155 | + |
| 156 | + function video_download(obj) { |
| 157 | + $.ajax({ |
| 158 | + url: window.location, |
| 159 | + type: "GET", |
| 160 | + success: function (res) { |
| 161 | + var video = $(res) |
| 162 | + .filter("meta[property='og:video']") |
| 163 | + .attr("content"); |
| 164 | + if (video) { |
| 165 | + $("video").each(function () { |
| 166 | + $(this).get(0).pause(); |
| 167 | + $(this).get(0).currentTime = 0; |
| 168 | + }); |
| 169 | + window.open(video, "_blank"); |
| 170 | + } |
| 171 | + }, |
| 172 | + error: function (res) { |
| 173 | + console.log(res); |
| 174 | + }, |
| 175 | + }); |
| 176 | + } |
| 177 | + |
| 178 | + function getBestImage(el) { |
| 179 | + el = el.jquery ? el : $(el); |
| 180 | + var img; |
| 181 | + var srcset = el.attr("srcset").split("w,"); |
| 182 | + if (srcset) { |
| 183 | + img = srcset.slice(-1)[0].split(" ")[0]; |
| 184 | + // console.log("SrcSet P",img); |
| 185 | + } else { |
| 186 | + img = el.attr("src"); |
| 187 | + // console.log("Normal P",img); |
| 188 | + } |
| 189 | + return img; |
| 190 | + } |
| 191 | + |
| 192 | + function cleanFilename(file) { |
| 193 | + return file.replace("jpg", "mp4").split("?")[0].split("/").pop(); |
| 194 | + } |
| 195 | + |
| 196 | + function stopVideo() { |
| 197 | + $("video").each(function () { |
| 198 | + $(this).get(0).pause(); |
| 199 | + $(this).get(0).currentTime = 0; |
| 200 | + }); |
| 201 | + } |
| 202 | + |
| 203 | + /* credits: @jomifepe */ |
| 204 | + function getCurrentItem(el) { |
| 205 | + var allitems = document.querySelectorAll(".Yi5aA"); |
| 206 | + for (let i = 0; i < allitems.length; i++) { |
| 207 | + if (allitems[i].classList.contains("XCodT")) { |
| 208 | + return i; |
| 209 | + } |
| 210 | + } |
| 211 | + return -1; |
| 212 | + } |
| 213 | + |
| 214 | + function fetchVideoURL(baseUrl, videoElem, t) { |
| 215 | + GM_xmlhttpRequest({ |
| 216 | + method: "GET", |
| 217 | + url: baseUrl.split("?")[0] + "?__a=1", |
| 218 | + synchronous: false, |
| 219 | + onload: function (response) { |
| 220 | + var result = JSON.parse(response.responseText); |
| 221 | + let postData = result?.graphql?.shortcode_media; |
| 222 | + var current = getCurrentItem(); |
| 223 | + if (current >= 0) |
| 224 | + postData = |
| 225 | + postData?.edge_sidecar_to_children?.edges?.[current]?.node; |
| 226 | + if (!postData) throw "No post data found"; |
| 227 | + if (!postData.is_video) throw "Post is not a video"; |
| 228 | + if (!postData.video_url) throw "No video url found"; |
| 229 | + let videoUrl = postData.video_url; |
| 230 | + videoElem.setAttribute("videoURL", videoUrl); |
| 231 | + window.open(videoUrl, t); |
| 232 | + return true; |
| 233 | + }, |
| 234 | + }); |
| 235 | + } |
| 236 | + |
| 237 | + /* left-click and hold shift key to open desired item */ |
| 238 | + $(document).on("click", alt_trigger, function (e, a) { |
| 239 | + e = window.event ? event : e; |
| 240 | + if (e.shiftKey) despecial_ig(e, $(this), "rm"); |
| 241 | + }); |
| 242 | + |
| 243 | + /* keyboard shortcut alt+f(ullsize) works on video popup, single photo, single video pages */ |
| 244 | + $(document).on("ig_press", ig, function (e, a) { |
| 245 | + despecial_ig(e, $(this), a); |
| 246 | + }); |
| 247 | + |
| 248 | + document.addEventListener("keydown", function (e) { |
| 249 | + e = window.event ? event : e; |
| 250 | + if (e.keyCode == 70 && e.altKey) $(ig).trigger("ig_press", ["rm"]); |
| 251 | + }); |
| 252 | + })(jQuery); |
| 253 | + }, |
| 254 | +}; |
0 commit comments