InsCNM

Automatically fetch media information from Instagram URLs.

当前为 2024-10-10 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         InsCNM
// @namespace    http://tampermonkey.net/
// @version      4.4
// @description  Automatically fetch media information from Instagram URLs.
// @author       Belugu
// @match        https://www.instagram.com/p/*
// @grant        GM_xmlhttpRequest
// @require      https://code.jquery.com/jquery-3.6.0.min.js
// @icon         https://iili.io/29TPCR1.jpg
// ==/UserScript==

(function() {
  // Main logic
  $(document).ready(async function() {
    if (window.location.href.match(/^https:\/\/www\.instagram\.com\/p\/[a-zA-Z0-9_-]+\/$/)) {
      const shortcode = extractShortcodeFromURL(window.location.href);
      if (shortcode) {
        const mediaInfo = await fetchMediaInfoByShortcode(shortcode);
        if (mediaInfo) {
          const { taken_at, comment_count, like_count } = mediaInfo.items[0];
          showMediaInfoUI(`
            <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>
            <p>评论数: ${comment_count} <button class="copy-btn" data-copy="${comment_count}" style="background:none; border:none; cursor:pointer;">🗿</button></p>
            <p>点赞数: ${like_count} <button class="copy-btn" data-copy="${like_count}" style="background:none; border:none; cursor:pointer;">🗿</button></p>
          `);
        } else {
          showMediaInfoUI('<p>获取媒体信息失败。</p>');
        }
      }
    }
  });

function showMediaInfoUI(mediaInfoHtml) {
  const container = $(
    `<div id="instagram-fetcher-ui" style="position:fixed; top:20px; right:-350px; background:white; color:black; border:1px solid #ccc; padding:15px; z-index:10000; width:300px; border-radius:10px;">
      <div style="display: flex; justify-content: space-between; align-items: center;">
        <h3 style="color:black; margin:0;">输入网址后按 Enter 搜索</h3>
        <button id="close-ui-button" style="background:none; border:none; font-size:20px; cursor:pointer; color:black;">&times;</button>
      </div>
      <div id="input-area" style="display: flex; align-items: center; margin-top:10px;">
        <input type="text" id="instagram-url-input" placeholder="输入网址" style="width:80%; padding:5px; color:black; border:1px solid #ccc; border-radius:5px; box-shadow:none; outline:none;"/>
      </div>
      <div id="media-info-output" style="margin-top:15px; font-size:14px;">${mediaInfoHtml}</div>
    </div>`
  );


    $('body').append(container);
    $('#instagram-fetcher-ui').animate({ right: '20px' }, 400);

    $('#instagram-url-input').on('keypress', async function(e) {
      if (e.which === 13) { // Enter key pressed
        const url = $('#instagram-url-input').val();
        if (url) {
          const shortcode = extractShortcodeFromURL(url);
          if (shortcode) {
            $('#media-info-output').text('正在获取媒体信息...');
            const mediaInfo = await fetchMediaInfoByShortcode(shortcode);
            if (mediaInfo) {
              const { taken_at, comment_count, like_count } = mediaInfo.items[0];
              $('#media-info-output').html(
                `<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>
                 <p>评论数: ${comment_count} <button class="copy-btn" data-copy="${comment_count}" style="background:none; border:none; cursor:pointer;">🗿</button></p>
                 <p>点赞数: ${like_count} <button class="copy-btn" data-copy="${like_count}" style="background:none; border:none; cursor:pointer;">🗿</button></p>`
              );
            } else {
              $('#media-info-output').text('获取媒体信息失败。');
            }
          } else {
            $('#media-info-output').text('无效的 Instagram URL。');
          }
        }
      }
    });

    $('#close-ui-button').click(function() {
      $(this).css({ transform: 'scale(0.95)' });
      setTimeout(() => $(this).css({ transform: 'scale(1)' }), 100);
      $('#instagram-fetcher-ui').animate({ right: '-350px' }, 400, function() {
        $(this).remove();
      });
    });

    $(document).on('click', '.copy-btn', function() {
      const textToCopy = $(this).data('copy');
      navigator.clipboard.writeText(textToCopy).then(() => {
        showNotification("已复制到剪贴板");
      }).catch(err => {
        console.error('复制失败:', err);
      });
    });
  }

  // Function to get a random gradient color
  function getRandomGradient() {
    const gradients = [
      '#6a11cb, #2575fc',  // Blue gradient
      '#11998e, #38ef7d',  // Green gradient
      '#ff7e5f, #feb47b',  // Original orange gradient
      '#ff6a00, #ee0979',  // Red gradient
      '#43cea2, #185a9d'   // Aqua gradient
    ];
    return gradients[Math.floor(Math.random() * gradients.length)];
  }

  // Function to show a simple notification with animation
  function showNotification(message) {
    const gradient = getRandomGradient(); // Get the gradient before usage
    const notification = $(
      `<div class="notification" style="position:fixed; bottom:-60px; right:20px; background: linear-gradient(to right, ${gradient}); color:white; padding:10px; border-radius:5px; z-index:10001; opacity:0.9;">
        ${message}
      </div>`
    );

    // Adjust the position of existing notifications
    $('.notification').each(function() {
      const currentBottom = parseInt($(this).css('bottom'));
      $(this).css('bottom', currentBottom + 60 + 'px');
    });

    $('body').append(notification);
    notification.animate({ bottom: '40px', opacity: 1 }, 400).delay(3000).animate({ opacity: 0 }, 400, function() {
      $(this).remove();
    });
  }

  // Listen for Alt+N to toggle the input UI
  document.addEventListener('keydown', function (e) {
    if (e.altKey && e.key === 'n') {
      const container = $('#instagram-fetcher-ui');
      if (container.length) {
        container.find('#close-ui-button').click();
      } else {
        showMediaInfoUI('<p>输入网址后按 Enter 搜索。</p>');
      }
    }
  });

// Function to extract the shortcode from a given URL
function extractShortcodeFromURL(url) {
  try {
    const urlObj = new URL(url);
    const pathSegments = urlObj.pathname.split('/');
    if (pathSegments[1] === 'p' || pathSegments[1] === 'reel') {
      return pathSegments[2] ? pathSegments[2] : null;
    }
    return null;
  } catch (error) {
    console.error("Error extracting shortcode from URL:", error);
    return null;
  }
}


  // Function to fetch media info by shortcode
  async function fetchMediaInfoByShortcode(shortcode) {
    const mediaId = await getMediaID(shortcode);
    if (!mediaId) {
      console.error("Failed to fetch media ID.");
      $('#media-info-output').text('获取媒体 ID 失败,请稍后重试。');
      return null;
    }

    try {
      const mediaInfo = await getMediaInfo(mediaId);
      console.log("Media Info:", mediaInfo);
      return mediaInfo;
    } catch (error) {
      console.error("Error retrieving media info:", error);
      return null;
    }
  }

  // Function to fetch media ID using shortcode
  async function getMediaID(shortcode) {
    try {
      const response = await fetch(`https://www.instagram.com/p/${shortcode}/`, {
        headers: {
          "User-Agent": window.navigator.userAgent,
          "Accept": "text/html"
        }
      });
      if (!response.ok) {
        throw new Error(`HTTP error! status: ${response.status}`);
      }
      const html = await response.text();
      const mediaIdMatch = html.match(/"media_id":"(\d+)"/);
      if (mediaIdMatch) {
        return mediaIdMatch[1];
      } else {
        console.error("Media ID not found in page HTML.");
      }
    } catch (error) {
      console.error("Error fetching media ID:", error);
    }
    return null;
  }

  // Function to get app ID
  function getAppID() {
    let result = null;
    $('script[type="application/json"]').each(function() {
      const regexp = /"APP_ID":"([0-9]+)"/ig;
      const matcher = $(this).text().match(regexp);
      if (matcher != null && result == null) {
        result = [...$(this).text().matchAll(regexp)];
      }
    });
    return (result) ? result.at(0).at(-1) : null;
  }

  // Function to get media info using media ID
  async function getMediaInfo(mediaId) {
    return new Promise((resolve, reject) => {
      let getURL = `https://i.instagram.com/api/v1/media/${mediaId}/info/`;

      if (mediaId == null) {
        console.error("Cannot call Media API because the media ID is invalid.");
        reject("Cannot call Media API because the media ID is invalid.");
        return;
      }

      GM_xmlhttpRequest({
        method: "GET",
        url: getURL,
        headers: {
          "User-Agent": window.navigator.userAgent,
          "Accept": "application/json",
          'X-IG-App-ID': getAppID()
        },
        onload: function (response) {
          try {
            if (response.finalUrl == getURL) {
              let obj = JSON.parse(response.responseText);
              resolve(obj);
            } else {
              let finalURL = new URL(response.finalUrl);
              if (finalURL.pathname.startsWith('/accounts/login')) {
                console.error("The account must be logged in to access Media API.");
              } else {
                console.error('Unable to retrieve content because the API was redirected to "' + response.finalUrl + '"');
              }
              reject(-1);
            }
          } catch (error) {
            console.error("Error parsing JSON response:", error);
            reject(error);
          }
        },
        onerror: function (err) {
          reject(err);
        }
      });
    });
  }
})();