您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Add Date and Location in instagram Posts by sniffing background requests
当前为
// ==UserScript== // @name Instagram Post Date and Location // @namespace http://tampermonkey.net/ // @version 1.7 // @description Add Date and Location in instagram Posts by sniffing background requests // @author SH3LL // @match https://www.instagram.com/* // @grant GM_addStyle // @run-at document-end // ==/UserScript== (function() { 'use strict'; GM_addStyle(` .post-date-label { position: absolute; top: 10px; left: 10px; background-color: rgba(0, 0, 0, 0.7); color: white; padding: 5px; border-radius: 5px; font-size: 12px; z-index: 99999; } .post-location-label { position: absolute; top: 10px; right: 10px; background-color: rgba(0, 0, 0, 0.7); color: white; padding: 5px; border-radius: 5px; font-size: 12px; z-index: 99999; } `); const interceptedJsons = []; const postIndex = {}; // map: code -> node // Intercept XHR GraphQL requests and index nodes containing "code" const originalSend = XMLHttpRequest.prototype.send; XMLHttpRequest.prototype.send = function() { this.addEventListener('readystatechange', function() { try { if (this.readyState === 4 && this.responseURL && this.responseURL.includes('/graphql/query')) { console.log('[DEBUG] Intercepted GraphQL call:', this.responseURL); let json; try { json = JSON.parse(this.responseText); } catch (err) { console.error('[DEBUG] Error parsing JSON from responseText', err); return; } console.log('[DEBUG] JSON root keys:', Object.keys(json || {})); interceptedJsons.push(json); const indexed = indexJsonNodes(json); const n = Object.keys(indexed).length; console.log(`[DEBUG] Indexed ${n} media nodes from this GraphQL response`); Object.assign(postIndex, indexed); } } catch (outerErr) { console.error('[DEBUG] Error in XHR event listener', outerErr); } }); return originalSend.apply(this, arguments); }; // Recursively scan JSON and collect media objects (objects with "code" + timestamp) function indexJsonNodes(json) { const map = {}; function walk(obj) { if (!obj || typeof obj !== 'object') return; if (Array.isArray(obj)) { obj.forEach(walk); return; } // heuristic: media object has "code" and "taken_at" (or similar) if (obj.code && (obj.taken_at || obj.taken_at_timestamp || obj.taken_at_ms)) { map[obj.code] = obj; } for (const k in obj) { if (!obj.hasOwnProperty || obj.hasOwnProperty(k)) walk(obj[k]); } } walk(json && json.data ? json.data : json); return map; } // Robust extraction of post code from href function extractPostCodeFromHref(href) { if (!href) return null; href = String(href); // regex: /p/<code>/ or /reel/<code>/ or /tv/<code>/ const regex = /\/(?:p|reel|tv)\/([^\/?#]+)/i; const m = href.match(regex); if (m && m[1]) { console.log('[DEBUG] Regex extracted postCode:', m[1], 'from href:', href); return m[1]; } // fallback: split and look for 'p'/'reel'/'tv' and take the next segment const parts = href.split('/').filter(Boolean); console.log('[DEBUG] href parts fallback:', parts); for (let i = 0; i < parts.length - 1; i++) { const seg = parts[i].toLowerCase(); if (seg === 'p' || seg === 'reel' || seg === 'tv') { console.log('[DEBUG] Fallback extracted postCode:', parts[i+1], 'from parts:', parts); return parts[i+1]; } } console.log('[DEBUG] No postCode extracted from href:', href); return null; } function addDateLabel(postElement, dateSeconds) { if (!postElement.querySelector('.post-date-label')) { console.log('[DEBUG] Adding date label for timestamp:', dateSeconds); const label = document.createElement('div'); label.classList.add('post-date-label'); label.textContent = "📅 " + formatDate(dateSeconds * 1000); postElement.style.position = 'relative'; postElement.appendChild(label); } else { console.log('[DEBUG] Date label already exists for this element'); } } function addLocationLabel(postElement, locationName, locationPk) { if (locationName && !postElement.querySelector('.post-location-label')) { console.log('[DEBUG] Adding location label:', locationName, 'pk:', locationPk); const label = document.createElement('a'); label.classList.add('post-location-label'); const truncated = locationName.length > 20 ? locationName.substring(0, 20) + "..." : locationName; label.textContent = `📌 ${truncated}`; label.href = `https://www.instagram.com/explore/locations/${locationPk}`; label.target = "_blank"; postElement.style.position = 'relative'; postElement.appendChild(label); } } function formatDate(ms) { const date = new Date(ms); const options = { year: 'numeric', month: 'short', day: 'numeric' }; return date.toLocaleDateString('en-US', options); } function processPosts() { try { // image wrapper selector (more stable) const postElements = document.querySelectorAll('._aagu'); console.log('[DEBUG] processPosts → postElements found:', postElements.length); postElements.forEach(postElement => { try { const linkElement = postElement.closest('a[href*="/p/"], a[href*="/reel/"], a[href*="/tv/"]'); if (!linkElement) { console.log('[DEBUG] No <a> post found for this wrapper'); return; } const href = linkElement.getAttribute('href'); console.log('[DEBUG] href found:', href); const postCode = extractPostCodeFromHref(href); if (!postCode) { console.log('[DEBUG] postCode not extracted, skipping this post'); return; } // look directly in the indexed map const node = postIndex[postCode]; if (node) { console.log('[DEBUG] Found node from postIndex for code:', postCode, node); const ts = node.taken_at || node.taken_at_timestamp || node.taken_at_ms; if (ts) addDateLabel(postElement, ts); else console.log('[DEBUG] Node has no visible timestamp:', node); if (node.location) { const name = node.location.name || node.location.title || ''; const pk = node.location.pk || node.location.id || ''; addLocationLabel(postElement, name, pk); } else { console.log('[DEBUG] Node has no location:', node); } } else { // fallback: scan intercepted JSONs console.log('[DEBUG] No node in postIndex for code:', postCode, '→ trying fallback scan'); let found = null; for (const j of interceptedJsons) { const candidate = findNodeWithCode(j, postCode); if (candidate) { found = candidate; break; } } if (found) { console.log('[DEBUG] Found node via fallback scan:', found); const ts2 = found.taken_at || found.taken_at_timestamp || found.taken_at_ms; if (ts2) addDateLabel(postElement, ts2); if (found.location) addLocationLabel(postElement, found.location.name || '', found.location.pk || found.location.id || ''); } else { console.log('[DEBUG] Node for code not found in any intercepted JSON:', postCode); } } } catch (inner) { console.error('[DEBUG] Error processing a single postElement', inner); } }); } catch (err) { console.error('[DEBUG] Error in processPosts', err); } } // Recursive search for object with property code === target function findNodeWithCode(obj, target) { if (!obj || typeof obj !== 'object') return null; if (Array.isArray(obj)) { for (const item of obj) { const r = findNodeWithCode(item, target); if (r) return r; } return null; } if (obj.code === target) return obj; for (const k in obj) { if (obj.hasOwnProperty && obj.hasOwnProperty(k)) { const r = findNodeWithCode(obj[k], target); if (r) return r; } } return null; } // initial run + observer console.log('[DEBUG] Script started - running first processPosts'); processPosts(); const observer = new MutationObserver((mutations) => { console.log('[DEBUG] DOM mutation detected → processPosts'); processPosts(); }); observer.observe(document.body, { childList: true, subtree: true }); })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址