InsCNM

获取INS数据!

  1. // ==UserScript==
  2. // @name InsCNM
  3. // @namespace http://tampermonkey.net/
  4. // @version 5.7
  5. // @description 获取INS数据!
  6. // @author Belugu
  7. // @match https://www.instagram.com/p/*
  8. // @match https://www.instagram.com/reel/*
  9. // @match https://www.instagram.com/*
  10. // @grant GM_xmlhttpRequest
  11. // @grant GM_getResourceText
  12. // @grant GM_addStyle
  13. // @require https://code.jquery.com/jquery-3.6.0.min.js
  14. // @require https://cdnjs.cloudflare.com/ajax/libs/toastify-js/1.12.0/toastify.min.js
  15. // @resource TOASTIFY_CSS https://cdn.jsdelivr.net/npm/toastify-js/src/toastify.min.css
  16. // @icon https://iili.io/29TPCR1.jpg
  17. // ==/UserScript==
  18.  
  19. (function() {
  20. 'use strict';
  21.  
  22. // 添加 Toastify 的 CSS 样式
  23. GM_addStyle(GM_getResourceText("TOASTIFY_CSS"));
  24.  
  25. // Main logic
  26. $(document).ready(async function() {
  27. if (window.location.href.match(/^https:\/\/www\.instagram\.com\/(p|reel)\/[a-zA-Z0-9_-]+\/$/)) {
  28. const shortcode = extractShortcodeFromURL(window.location.href);
  29. if (shortcode) {
  30. const mediaInfo = await fetchMediaInfoByShortcode(shortcode);
  31. if (mediaInfo) {
  32. const { taken_at, comment_count, like_count } = mediaInfo.items[0];
  33. const mediaInfoHtml = `
  34. <p>发布时间: ${new Date(taken_at * 1000).toLocaleString()} <button class="copy-btn" data-copy="${new Date(taken_at * 1000).toLocaleString()}" style="background:none; border:none; cursor:pointer;">📋</button></p>
  35. <p>评论数: ${comment_count} <button class="copy-btn" data-copy="${comment_count}" style="background:none; border:none; cursor:pointer;">📋</button></p>
  36. <p>点赞数: ${like_count} <button class="copy-btn" data-copy="${like_count}" style="background:none; border:none; cursor:pointer;">📋</button></p>
  37. `;
  38. showMediaInfoUI(mediaInfoHtml);
  39. } else {
  40. showMediaInfoUI('<p>获取媒体信息失败。</p>');
  41. }
  42. }
  43. } else {
  44. // 如果不是特定的帖子页面,显示默认界面
  45. showMediaInfoUI('<p>输入网址后按 Enter 搜索。</p>');
  46. }
  47. });
  48.  
  49. function showMediaInfoUI(mediaInfoHtml) {
  50. // 检查是否已经存在菜单界面
  51. if ($('#instagram-fetcher-ui').length) {
  52. // 如果存在,更新内容即可
  53. $('#media-info-output').html(mediaInfoHtml);
  54. return;
  55. }
  56.  
  57. const container = $(
  58. `<div id="instagram-fetcher-ui" style="
  59. position:fixed;
  60. top:20px;
  61. right:-350px;
  62. background: rgba(255, 255, 255, 0.7);
  63. color:black;
  64. border:1px solid #ccc;
  65. padding:15px;
  66. z-index:10000;
  67. width:350px;
  68. border-radius:10px;
  69. backdrop-filter: blur(10px);
  70. box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
  71. cursor: move;
  72. ">
  73. <div style="display: flex; justify-content: space-between; align-items: center;">
  74. <h3 style="color:black; margin:0; text-align:center; width:100%;">insCNM</h3>
  75. </div>
  76. <div id="input-area" style="margin-top:10px; width: 100%;">
  77. <input type="text" id="instagram-url-input" placeholder="输入网址" style="
  78. width: 100%;
  79. padding:5px;
  80. margin: 0;
  81. border:1px solid #ccc;
  82. border-radius:5px;
  83. box-sizing: border-box; /* 添加这行 */
  84. background: rgba(255, 255, 255, 0.8);
  85. " />
  86. </div>
  87. <div id="media-info-output" style="
  88. margin-top:15px;
  89. font-size:14px;
  90. text-align: left;
  91. width: 100%;
  92. ">${mediaInfoHtml}</div>
  93. </div>`
  94. );
  95.  
  96. $('body').append(container);
  97. $('#instagram-fetcher-ui').animate({ right: '20px' }, 400);
  98.  
  99. // 调用函数使菜单可拖动
  100. dragElement(document.getElementById("instagram-fetcher-ui"));
  101.  
  102. $('#instagram-url-input').on('click', function() {
  103. $(this).focus();
  104. });
  105.  
  106. $('#instagram-url-input').on('keyup', async function(e) {
  107. if (e.which === 13) { // Enter key pressed
  108. const url = $('#instagram-url-input').val();
  109. console.log("User entered URL:", url);
  110. if (url) {
  111. const shortcode = extractShortcodeFromURL(url);
  112. if (shortcode) {
  113. $('#media-info-output').slideUp(200, async function() {
  114. $('#media-info-output').text('正在获取媒体信息...');
  115. $(this).slideDown(200);
  116. });
  117. const mediaInfo = await fetchMediaInfoByShortcode(shortcode);
  118. if (mediaInfo) {
  119. const { taken_at, comment_count, like_count } = mediaInfo.items[0];
  120. $('#media-info-output').slideUp(200, function() {
  121. $('#media-info-output').html(
  122. `<p>发布时间: ${new Date(taken_at * 1000).toLocaleString()} <button class="copy-btn" data-copy="${new Date(taken_at * 1000).toLocaleString()}" style="background:none; border:none; cursor:pointer;">📋</button></p>
  123. <p>评论数: ${comment_count} <button class="copy-btn" data-copy="${comment_count}" style="background:none; border:none; cursor:pointer;">📋</button></p>
  124. <p>点赞数: ${like_count} <button class="copy-btn" data-copy="${like_count}" style="background:none; border:none; cursor:pointer;">📋</button></p>`
  125. );
  126. $(this).slideDown(400);
  127. });
  128. } else {
  129. $('#media-info-output').slideUp(200, function() {
  130. $('#media-info-output').text('获取媒体信息失败。').slideDown(400);
  131. });
  132. }
  133. } else {
  134. $('#media-info-output').slideUp(200, function() {
  135. $('#media-info-output').text('无效的 Instagram URL。').slideDown(400);
  136. });
  137. }
  138. }
  139. }
  140. });
  141.  
  142. $(document).on('click', '.copy-btn', function() {
  143. const textToCopy = $(this).data('copy');
  144. navigator.clipboard.writeText(textToCopy).then(() => {
  145. showNotification("已复制到剪贴板");
  146. }).catch(err => {
  147. console.error('复制失败:', err);
  148. });
  149. });
  150. }
  151.  
  152. function showNotification(message) {
  153. Toastify({
  154. text: message,
  155. duration: 3000,
  156. close: true,
  157. gravity: 'top',
  158. position: 'center',
  159. style: {
  160. background: getRandomGradientColor(),
  161. color: '#FFFFFF',
  162. borderRadius: '2px',
  163. },
  164. stopOnFocus: true,
  165. }).showToast();
  166. }
  167.  
  168. function getRandomGradientColor() {
  169. const gradients = [
  170. 'linear-gradient(120deg, #89f7fe 0%, #66a6ff 100%)',
  171. 'linear-gradient(to right, #d4fc79, #96e6a1)',
  172. 'linear-gradient(to right, #ff0844, #ffb199)',
  173. 'linear-gradient(to right, #f83600, #f9d423)',
  174. 'linear-gradient(to right, #00cdac, #8ddad5)',
  175. 'linear-gradient(to right, #df89b5, #bfd9fe)',
  176. 'linear-gradient(to right, #d7d2cc, #304352)',
  177. 'linear-gradient(to right, #c1c161, #d4d4b1)',
  178. 'linear-gradient(to right, #20E2D7, #F9FEA5)',
  179. 'linear-gradient(to right, #8360c3, #2ebf91)'
  180. ];
  181. return gradients[Math.floor(Math.random() * gradients.length)];
  182. }
  183.  
  184. document.addEventListener('keydown', function (e) {
  185. if (e.altKey && e.key === 'n') {
  186. const container = $('#instagram-fetcher-ui');
  187. if (container.length) {
  188. container.css({
  189. transition: 'all 0.5s ease',
  190. filter: 'blur(10px)',
  191. opacity: '0'
  192. });
  193. setTimeout(() => container.remove(), 500);
  194. } else {
  195. showMediaInfoUI('<p>输入网址后按 Enter 搜索。</p>');
  196. }
  197. }
  198. });
  199.  
  200. function extractShortcodeFromURL(url) {
  201. try {
  202. const urlObj = new URL(url);
  203. const pathSegments = urlObj.pathname.split('/');
  204. console.log("URL Object:", urlObj);
  205. console.log("Path Segments:", pathSegments);
  206. if (pathSegments[1] === 'p' || pathSegments[1] === 'reel') {
  207. return pathSegments[2] ? pathSegments[2] : null;
  208. }
  209. return null;
  210. } catch (error) {
  211. console.error("Error extracting shortcode from URL:", error);
  212. return null;
  213. }
  214. }
  215.  
  216. async function fetchMediaInfoByShortcode(shortcode) {
  217. const mediaId = await getMediaID(shortcode);
  218. if (!mediaId) {
  219. console.error("Failed to fetch media ID.");
  220. $('#media-info-output').text('获取媒体 ID 失败,请稍后重试。');
  221. return null;
  222. }
  223.  
  224. try {
  225. const mediaInfo = await getMediaInfo(mediaId);
  226. console.log("Media Info:", mediaInfo);
  227. return mediaInfo;
  228. } catch (error) {
  229. console.error("Error retrieving media info:", error);
  230. return null;
  231. }
  232. }
  233.  
  234. async function getMediaID(shortcode) {
  235. try {
  236. const response = await fetch(`https://www.instagram.com/p/${shortcode}/`, {
  237. headers: {
  238. "User-Agent": window.navigator.userAgent,
  239. "Accept": "text/html"
  240. }
  241. });
  242. if (!response.ok) {
  243. throw new Error(`HTTP error! status: ${response.status}`);
  244. }
  245. const html = await response.text();
  246. const mediaIdMatch = html.match(/"media_id":"(\d+)"/);
  247. if (mediaIdMatch) {
  248. return mediaIdMatch[1];
  249. } else {
  250. console.error("Media ID not found in page HTML.");
  251. }
  252. } catch (error) {
  253. console.error("Error fetching media ID:", error);
  254. }
  255. return null;
  256. }
  257.  
  258. function getAppID() {
  259. let result = null;
  260. $('script[type="application/json"]').each(function() {
  261. const regexp = /"APP_ID":"([0-9]+)"/ig;
  262. const matcher = $(this).text().match(regexp);
  263. if (matcher != null && result == null) {
  264. result = [...$(this).text().matchAll(regexp)];
  265. }
  266. });
  267. return (result) ? result.at(0).at(-1) : null;
  268. }
  269.  
  270. async function getMediaInfo(mediaId) {
  271. return new Promise((resolve, reject) => {
  272. let getURL = `https://i.instagram.com/api/v1/media/${mediaId}/info/`;
  273.  
  274. if (mediaId == null) {
  275. console.error("Cannot call Media API because the media ID is invalid.");
  276. reject("Cannot call Media API because the media ID is invalid.");
  277. return;
  278. }
  279.  
  280. GM_xmlhttpRequest({
  281. method: "GET",
  282. url: getURL,
  283. headers: {
  284. "User-Agent": window.navigator.userAgent,
  285. "Accept": "application/json",
  286. 'X-IG-App-ID': getAppID()
  287. },
  288. onload: function (response) {
  289. try {
  290. if (response.finalUrl == getURL) {
  291. let obj = JSON.parse(response.responseText);
  292. resolve(obj);
  293. } else {
  294. let finalURL = new URL(response.finalUrl);
  295. if (finalURL.pathname.startsWith('/accounts/login')) {
  296. console.error("The account must be logged in to access Media API.");
  297. } else {
  298. console.error('Unable to retrieve content because the API was redirected to "' + response.finalUrl + '"');
  299. }
  300. reject(-1);
  301. }
  302. } catch (error) {
  303. console.error("Error parsing JSON response:", error);
  304. reject(error);
  305. }
  306. },
  307. onerror: function (err) {
  308. reject(err);
  309. }
  310. });
  311. });
  312. }
  313.  
  314. // 使元素可拖动的函数
  315. function dragElement(element) {
  316. let pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
  317. if (element) {
  318. element.onmousedown = dragMouseDown;
  319. }
  320.  
  321. function dragMouseDown(e) {
  322. e = e || window.event;
  323. e.preventDefault();
  324. // 获取鼠标的初始位置
  325. pos3 = e.clientX;
  326. pos4 = e.clientY;
  327. document.onmouseup = closeDragElement;
  328. document.onmousemove = elementDrag;
  329. }
  330.  
  331. function elementDrag(e) {
  332. e = e || window.event;
  333. e.preventDefault();
  334. // 计算鼠标移动的距离
  335. pos1 = pos3 - e.clientX;
  336. pos2 = pos4 - e.clientY;
  337. pos3 = e.clientX;
  338. pos4 = e.clientY;
  339. // 设置元素的新位置
  340. element.style.top = (element.offsetTop - pos2) + "px";
  341. element.style.left = (element.offsetLeft - pos1) + "px";
  342. }
  343.  
  344. function closeDragElement() {
  345. // 停止拖动时清除事件监听
  346. document.onmouseup = null;
  347. document.onmousemove = null;
  348. }
  349. }
  350. })();