Creates a scrollable wall of full-size images from any user's instagram page, "tagged" page, or the instagram homepage. Just click the "Load Images" button at the top of the list of images/posts.
当前为
// ==UserScript==
// @name Instagram full-size media scroll wall
// @namespace driver8.net
// @author driver8
// @license GNU AGPLv3
// @description Creates a scrollable wall of full-size images from any user's instagram page, "tagged" page, or the instagram homepage. Just click the "Load Images" button at the top of the list of images/posts.
// @match *://*.instagram.com/*
// @version 0.2.0
// @grant none
// ==/UserScript==
(function() {
'use strict';
console.log('hi insta scroll');
// https://www.instagram.com/graphql/query/?query_hash=<hash>&variables={%22shortcode%22:%22<shortcode>%22}
// https://i.instagram.com/api/v1/usertags/0000000000/feed/?count=12&max_id=0000000000000000000
const IMAGES_PER_QUERY = 12;
const NTH_TO_LAST_IMAGE = 3;
const HEIGHT_PCT = .8;
const WIDTH_PCT = .49;
const VID_VOLUME = 0.02;
var MODE = 'profile';
var userId = document.body.innerHTML.match(/profilePage_(\d+)/)?.[1];
const win = window;
var notLoaded = true;
const tempDiv = document.createElement('div');
function pickMode() {
if (document.location.href.match(/https:\/\/(www\.)?instagram.com\/?($|#)/)) {
MODE = 'home';
getQueryHash();
} else if (document.location.href.match(/\/tagged\//)) {
MODE = 'tagged';
getUserId();
} else if (document.location.href.match(/\/explore\//)) {
MODE = 'explore';
console.log('"Explore" loading not implemented yet!');
//getQueryHash();
} else if (document.location.href.match(/https:\/\/(www\.)?instagram.com\/p\//)) {
MODE = 'post';
} else {
MODE = 'profile';
getUserId();
}
//console.log('MODE', MODE);
}
function getUserId() {
userId = userId || document.body.innerHTML.match(/profilePage_(\d+)/)?.[1];
userId = userId || document.body.innerHTML.match(/<a author_id="(\d+)" class="heKAw"/)?.[1];
if (userId) {
getQueryHash();
} else {
let req = indexedDB.open('redux');
req.onsuccess = function(evt) {
//console.log('req evt', evt);
let db = req.result;
let os = db.transaction("paths")?.objectStore("paths");
let req2 = db.transaction("paths")?.objectStore("paths")?.get('users.usernameToId');
req2.onsuccess = function(evt) {
//console.log('db evt', evt);
let result = req2?.result;
let userName = document.location.href.match(/https:\/\/(?:www\.)?instagram.com\/([^\/]{3,})/)?.[1];
console.log('userName', userName);
userId = result?.[userName];
if (userId) {
getQueryHash();
} else {
requestUserId();
//console.log("Couldn't find user ID!");
return;
}
};
};
}
}
function requestUserId() {
let loc = document.location.href;
if (loc.match(/https:\/\/(?:www\.)?instagram.com\/([^\/]{3,})\/?$/)) {
loc += '?__a=1';
fetch(loc)
.then(resp => {
return resp.json();
})
.then(json => {
console.log('userId json', json);
userId = json?.graphql?.user?.id;
if (userId) {
getQueryHash();
} else {
console.log("Couldn't find user ID!");
return;
}
});
} else {
console.log("URL doesn't match a profile page");
return;
}
}
function getQueryHash() {
let allScripts = Array.from(document.getElementsByTagName('script'));
let PostPageContainer = allScripts.find(el => el.src && el.src.match(/ProfilePageContainer.js/));
let ConsumerLibCommons = allScripts.find(el => el.src && el.src.match(/ConsumerLibCommons.js/));
let Consumer = allScripts.find(el => el.src && el.src.match(/Consumer.js/));
var query_hash = false,
query_id = false;
if (ConsumerLibCommons) {
fetch(ConsumerLibCommons.src)
.then(resp => {
//console.log('resp 1', resp);
return resp.text();
})
.then(text => {
query_id = text.match(/profilePosts\.byUserId\.get.*?queryId:"([a-f0-9]+)"/)?.[1]; //profilePosts.byUserId.get(n))||void 0===s?void 0:s.pagination},queryId:"e5555555555555555555555555555508"
let app_id = text.match(/instagramWebDesktopFBAppId='(\d+)'/)?.[1];
let asbd_id = text.match(/ASBD_ID='(\d+)'/)?.[1];
query_id && notLoaded && loadImages(query_id, query_hash, app_id, asbd_id);
});
}
if (PostPageContainer) {
fetch(PostPageContainer.src)
.then(resp => {
//console.log('resp 2', resp);
return resp.text();
})
.then(text => {
query_id = text.match(/profilePosts\.byUserId\.get.*?queryId:"([a-f0-9]+)"/)?.[1]; //profilePosts.byUserId.get(n))||void 0===s?void 0:s.pagination},queryId:"e5555555555555555555555555555508"
let app_id = text.match(/instagramWebDesktopFBAppId='(\d+)'/)?.[1];
let asbd_id = text.match(/ASBD_ID='(\d+)'/)?.[1];
query_id && notLoaded && loadImages(query_id, query_hash, app_id, asbd_id);
});
}
// l.pagination},queryId:"15b55555555555555555555555555551"
if (Consumer) {
fetch(Consumer.src)
.then(resp => {
//console.log('resp 3', resp);
return resp.text();
})
.then(text => {
query_id = text.match(/profilePosts\.byUserId\.get[^;]+queryId:\s*"([a-f0-9]+)"/)?.[1]; //text.match(/l\.pagination\},queryId:"([a-f0-9]+)"/); //const s="05555555555555555555555555555554",E="
let app_id = text.match(/instagramWebDesktopFBAppId='(\d+)'/)?.[1];
let asbd_id = text.match(/ASBD_ID='(\d+)'/)?.[1];
query_id && notLoaded && loadImages(query_id, query_hash, app_id, asbd_id);
});
}
}
// https://www.instagram.com/graphql/query/?query_hash=<queryhash>&variables=%7B%22id%22%3A%22<profle_id>%22%2C%22first%22%3A12%2C%22after%22%3A%22<after_code>%3D%3D%22%7D
function loadImages(query_id, query_hash, app_id=936619743392459, asbd_id=198387, after=null) {
notLoaded = false;
console.log('id', query_id, 'hash', query_hash);
// let userId = document.querySelector('head > meta[property="instapp:owner_user_id"]')?.content;
let imageListQueryUrl;
let init = { responseType: 'json', credentials: 'include', referrerPolicy: 'no-referrer'};
if (MODE == 'profile') {
if (!userId) {
console.log("Couldn't find user ID!", userId);
return;
}
let queryVariables = {"id": userId, "first": IMAGES_PER_QUERY};
if (after) queryVariables.after = after;
let queryVariablesString = encodeURIComponent(JSON.stringify(queryVariables));
imageListQueryUrl = `https://www.instagram.com/graphql/query/?query_hash=${query_id}&variables=${queryVariablesString}`;
}
if (MODE == 'tagged') {
if (!userId) {
console.log("Couldn't find user ID!", userId);
return;
}
imageListQueryUrl = `https://i.instagram.com/api/v1/usertags/${userId}/feed/?count=${IMAGES_PER_QUERY}`;
if (after) {
imageListQueryUrl += `&max_id=${after}`;
}
init.headers = {
'X-IG-App-ID': app_id,
'X-ASBD-ID': asbd_id
}
}
if (MODE == 'home') {
imageListQueryUrl = 'https://i.instagram.com/api/v1/feed/timeline/';
//let fd = new FormData();
let fd = new URLSearchParams();
fd.set('is_async_ads_rti', 0);
fd.set('is_async_ads_double_request', 0);
fd.set('rti_delivery_backend', 0);
fd.set('is_async_ads_in_headload_enabled', 0);
fd.set('devide_id', win._sharedData?.device_id);
if (after) {
fd.set('max_id', after);
}
init.body = fd;
init.method = 'POST';
init.headers = {
'X-IG-App-ID': app_id,
'X-ASBD-ID': asbd_id,
'X-CSRFToken': win._sharedData?.config.csrf_token
}
}
fetch(imageListQueryUrl, init)
.then(resp => {
//console.log('json resp', resp);
return resp.json();
})
.then(json => {
console.log('json', json);
let timelineMedia, end_cursor, mediaList;
timelineMedia = json.data?.user.edge_owner_to_timeline_media;
end_cursor = timelineMedia?.page_info.end_cursor || json.next_max_id;
mediaList = timelineMedia?.edges.map(n => n.node) || json.items || json.feed_items?.map(n => n.media_or_ad).filter(n => n);
console.log('end_cursor', end_cursor, 'media list', mediaList);
let bigContainer = document.querySelector('#igBigContainer');
// Create the main container if it doesn't exist
if (!bigContainer) {
tempDiv.innerHTML = `<div id="igBigContainer" style="background-color: #112;width: 100%;height: 100%;z-index: 999;position: fixed;top: 0;left: 0;overflow: scroll;">
<div id="igAllImages" style="display:block; text-align:center;"></div></div>`;
bigContainer = tempDiv.firstElementChild;
document.body.innerHTML = '';
document.body.appendChild(bigContainer);
let imgStyle = document.createElement('style');
imgStyle.type = 'text/css';
setMaxSize(imgStyle);
document.body.appendChild(imgStyle);
window.addEventListener('resize', evt => setMaxSize(imgStyle));
styleIt();
}
let innerContainer = bigContainer.firstElementChild;
for (let media of mediaList) {
addMedia(media, innerContainer);
}
if (end_cursor) {
//console.log('end_cursor', end_cursor);
let triggerImage = document.querySelector('#igAllImages a:nth-last-of-type(3)');
bigContainer.onscroll = (evt) => {
let vh = document.documentElement.clientHeight || window.innerHeight || 0;
if (triggerImage.getBoundingClientRect().top - 800 < vh) {
bigContainer.onscroll = null;
console.log('loading next set of images');
loadImages(query_id, query_hash, app_id, asbd_id, end_cursor);
}
}
}
});
}
function getBestImage(media) {
let bestUrl = null;
let bestSize = 0;
let list = media?.display_resources || media?.image_versions2?.candidates;;
for (let m of list) {
let w = m?.width || m?.config_width || 0;
let h = m?.height || m?.config_height || 0;
let size = Math.max(w, h);
//console.log('size', size, m);
if (size > bestSize) {
bestSize = size;
bestUrl = m?.url || m?.src;
}
}
//best = media?.display_resources?.reduce((a, b) => Math.max(a.width, a.height) > Math.max(b.width, b.height) ? a : b).src;
return bestUrl;
}
function addMedia(media, container) {
let shortcode = media?.shortcode || media?.code;
let medias = media?.edge_sidecar_to_children?.edges?.map(n => n.node) ||
media?.carousel_media ||
[ media ];
for (let m of medias) {
let a = document.createElement('a');
a.href = `https://www.instagram.com/p/${shortcode}/`;
//console.log('m', m);
if (m?.is_video || m?.is_unified_video || m?.video_duration) {
tempDiv.innerHTML = `<div class="vidDiv"></div>`;
let vidDiv = tempDiv.firstElementChild;
let vid = document.createElement('video');
vid.src = m?.video_url || m?.video_versions?.reduce((a, b) => Math.max(a.width, a.height) > Math.max(b.width, b.height) ? a : b)?.url;
vid.controls = true;
vid.volume = VID_VOLUME;
a.textContent = 'Link';
vidDiv.appendChild(vid);
vidDiv.appendChild(a);
container.appendChild(vidDiv);
} else {
a.innerHTML += `<img src="${getBestImage(m)}">`;
container.appendChild(a);
}
}
}
function setMaxSize(userStyle) {
let vw = document.documentElement.clientWidth || window.innerWidth || 0;
let vh = document.documentElement.clientHeight || window.innerHeight || 0;
userStyle.innerHTML = `
#igAllImages img, #igAllImages video {
max-height: ${vh * HEIGHT_PCT}px;
max-width: ${vw * WIDTH_PCT}px;
}
`;
}
function styleIt() {
let userStyle = document.createElement('style');
userStyle.type = 'text/css';
userStyle.innerHTML = `
#igAllImages video {
border: green solid 2px;
}
#igAllImages .vidDiv {
display: inline-block;
}
#igAllImages .vidDiv a {
display: block;
text-decoration: none;
margin-top: -5px;
}
`;
document.body.appendChild(userStyle);
}
function startUp() {
(function insertButton() {
let loadButton = document.querySelector("#loadallbutton");
if (!loadButton) {
let insAt = document.querySelector('div.fx7hk, div[role*=tablist]'); //, .ENC4C, .ZcHy5, ._47KiJ');
if (insAt) {
tempDiv.innerHTML = profileButton;
loadButton = tempDiv.firstElementChild;
loadButton.onclick = pickMode;
insAt.appendChild(loadButton);
} else {
insAt = document.querySelector('article._8Rm4L')?.parentNode;
insAt = insAt || document.querySelector('main > div.MxEZm');
if (insAt) {
tempDiv.innerHTML = homeButton;
loadButton = tempDiv.firstElementChild;
loadButton.onclick = pickMode;
insAt.prepend(loadButton);
}
}
}
window.setTimeout(insertButton, 50);
})();
}
const profileButton = `<a aria-selected="false" class="_9VEo1 " role="tab" tabindex="0" id="loadallbutton"><span class="qzihg"><span class="_08DtY">Load Images</span></span></a>`;
const homeButton = `<article class="_8Rm4L bLWKA M9sTE _1gNme L_LMM SgTZ1 Tgarh ePUX4" role="presentation" tabindex="-1" style="cursor: pointer;" id="loadallbutton">
<div class="qF0y9 Igw0E IwRSH YBx95 _4EzTm" style="max-height: inherit; max-width: inherit;">
<div class="UE9AK">
<div class="qF0y9 Igw0E rBNOH CcYR1 ybXk5 _4EzTm">
<header class="Ppjfr">
<div class="Jv7Aj mArmR pZp3x">
<div class="RR-M- mrq0Z" aria-disabled="true" role="button" tabindex="-1">
<svg aria-label="" class="_8-yf5" color="#262626" fill="#262626" role="img" width="22" height="22" viewBox="0 0 24 20">
<rect fill="none" height="18" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" width="18" x="3" y="3"></rect>
<line fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" x1="9.015" x2="9.015" y1="3" y2="21"></line>
<line fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" x1="14.985" x2="14.985" y1="3" y2="21"></line>
<line fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" x1="21" x2="3" y1="9.015" y2="9.015"></line>
<line fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" x1="21" x2="3" y1="14.985" y2="14.985"></line>
</svg>
</div>
</div>
<div class="o-MQd z8cbW">
<div class="RqtMr">
<div class="e1e1d">
<span class="Jv7Aj mArmR MqpiF"><a class="sqdOP yWX7d _8A5w5 ZIAjV" tabindex="0">Load Images</a></span>
</div>
</div>
</div>
</header>
</div>
</div>
</div>
</article>
`;
startUp();
})();