Skip to content

Instantly share code, notes, and snippets.

@Far-Se
Created March 9, 2026 14:07
Show Gist options
  • Select an option

  • Save Far-Se/417f5a0c579d2670531cc35819e990cf to your computer and use it in GitHub Desktop.

Select an option

Save Far-Se/417f5a0c579d2670531cc35819e990cf to your computer and use it in GitHub Desktop.
Instagram Video Controls and Video Downloader.
// ==UserScript==
// @name Instagram Media Tools
// @namespace http://tampermonkey.net/
// @version 1.0.0
// @description Enhanced Instagram media viewer with keyboard shortcuts and download capabilities
// @author Your Name
// @match https://www.instagram.com/*
// @icon https://icons.duckduckgo.com/ip2/instagram.com.ico
// @grant GM_xmlhttpRequest
// @grant GM_openInTab
// @grant GM_registerMenuCommand
// ==/UserScript==
(function() {
'use strict';
// ==================== CONSTANTS ====================
const OPERATIONS = Object.freeze({
DOWNLOAD: 0,
OPEN: 1,
COPY: 2
});
const SELECTORS = Object.freeze({
VIDEO: 'video',
REEL_VIDEO: 'div[style="width: 100%;"] > div:not([class]) a[href*="reel"] video',
POST_LINK: 'a[href*="/p/"]',
ARTICLE: 'article'
});
const KEYBOARD_SHORTCUTS = Object.freeze({
SEEK_BACKWARD: [',', 'ArrowLeft'],
SEEK_FORWARD: ['.', 'ArrowRight'],
PLAY_PAUSE: [' '],
OPEN_IMAGE: ['r'],
OPEN_MEDIA: ['e']
});
const CONFIG = Object.freeze({
SEEK_DURATION: 1.5,
PROGRESS_UPDATE_INTERVAL: 50,
INIT_DELAY: 400,
MIN_PROGRESS_WIDTH: 480,
MAX_PROGRESS_WIDTH: 301
});
// ==================== UTILITY FUNCTIONS ====================
/**
* Extracts the shortcode from the current Instagram URL
* @returns {string|null} The shortcode or null if not found
*/
function getShortcodeFromUrl() {
const pathParts = window.location.pathname.split('/');
return pathParts.at(-2) || null;
}
/**
* Checks if an element is visible in the viewport
* @param {HTMLElement} element - The element to check
* @param {boolean} partiallyVisible - Whether partial visibility counts
* @returns {boolean} True if element is visible
*/
function isElementVisibleInViewport(element, partiallyVisible = false) {
const { top, left, bottom, right } = element.getBoundingClientRect();
const { innerHeight, innerWidth } = window;
if (partiallyVisible) {
const verticallyVisible = (top > 0 && top < innerHeight) ||
(bottom > 0 && bottom < innerHeight);
const horizontallyVisible = (left > 0 && left < innerWidth) ||
(right > 0 && right < innerWidth);
return verticallyVisible && horizontallyVisible;
}
return top >= 0 && left >= 0 && bottom <= innerHeight && right <= innerWidth;
}
/**
* Extracts the best quality source from an image's srcset attribute
* @param {HTMLImageElement} element - The image element
* @returns {string} The best quality image URL
*/
function getBestSourceFromSrcset(element) {
if (!element.hasAttribute('srcset')) {
return element.getAttribute('src') || '';
}
const srcset = element.getAttribute('srcset');
const candidates = srcset.split(',').map(item => {
const [url, descriptor] = item.trim().split(/\s+/);
return { url, descriptor: descriptor || '' };
});
let maxValue = 0;
let bestUrl = '';
candidates.forEach(({ url, descriptor }) => {
if (descriptor.endsWith('w')) {
const width = parseInt(descriptor.slice(0, -1), 10);
if (width > maxValue) {
maxValue = width;
bestUrl = url;
}
} else if (descriptor.endsWith('x')) {
const density = parseFloat(descriptor.slice(0, -1));
if (density > maxValue) {
maxValue = density;
bestUrl = url;
}
} else if (!bestUrl) {
bestUrl = url;
}
});
return bestUrl || element.getAttribute('src') || '';
}
/**
* Extracts video URL from Instagram's page HTML
* @param {string} html - The HTML content to search
* @returns {string|null} The video URL or null if not found
*/
function extractVideoUrlFromHtml(html) {
try {
const match = html.match(/RelayPrefetchedStreamCache.*?"url":"([^"]+mp4[^"]+)/);
if (match && match[1]) {
return match[1].replace(/\\+/g, '');
}
} catch (error) {
console.error('Error extracting video URL:', error);
}
return null;
}
/**
* Gets the currently visible video element
* @returns {HTMLVideoElement|null} The visible video element or null
*/
function getCurrentVideo() {
const videos = document.querySelectorAll(SELECTORS.VIDEO);
for (const video of videos) {
if (isElementVisibleInViewport(video)) {
return video;
}
}
return null;
}
/**
* Gets the last hovered element (useful for context menus)
* @returns {HTMLElement|null} The hovered element or null
*/
function getLastHoveredElement() {
const hoverElements = document.querySelectorAll(':hover');
return hoverElements[hoverElements.length - 2] || null;
}
// ==================== DOWNLOAD FUNCTIONS ====================
/**
* Downloads a file from a URL
* @param {string} url - The URL to download from
* @param {string} filename - The filename to save as
*/
function downloadFromUrl(url, filename = 'video.mp4') {
fetch(url)
.then(response => response.blob())
.then(blob => {
const blobUrl = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = blobUrl;
link.download = filename;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
URL.revokeObjectURL(blobUrl);
})
.catch(error => {
console.error('Download failed:', error);
handleMediaError(error);
});
}
/**
* Handles errors by opening a fallback link
* @param {Error} error - The error that occurred
*/
function handleMediaError(error) {
console.error('Media operation failed:', error);
const shortcode = getShortcodeFromUrl();
if (!shortcode) {
console.error('No shortcode found in URL');
return;
}
const fallbackLink = `https://www.instagram.com/p/${shortcode}/#downloadVideo`;
GM_openInTab(fallbackLink, { loadInBackground: true });
}
// ==================== VIDEO PLAYER FUNCTIONS ====================
/**
* Creates or updates a progress bar for a video element
* @param {HTMLVideoElement} video - The video element
*/
function createOrUpdateProgressBar(video) {
const videoParent = video.closest('div');
if (!videoParent) return;
const rect = video.getBoundingClientRect();
let progressElement = videoParent.querySelector('progress');
if (progressElement) {
progressElement.value = (video.currentTime / video.duration) * 100;
return;
}
// Create new progress element
progressElement = document.createElement('progress');
progressElement.className = 'progress progress1';
progressElement.max = 100;
progressElement.value = 0;
progressElement.textContent = 'Progress';
const width = rect.width < CONFIG.MAX_PROGRESS_WIDTH ?
CONFIG.MIN_PROGRESS_WIDTH : rect.width;
progressElement.style.width = `${width}px`;
// Add click handler for seeking
progressElement.onclick = (event) => {
const targetVideo = event.target.closest('div').querySelector('video');
if (targetVideo) {
const clickPosition = event.pageX - targetVideo.getBoundingClientRect().left;
const seekPercentage = clickPosition / targetVideo.offsetWidth;
targetVideo.currentTime = targetVideo.duration * seekPercentage;
}
};
videoParent.appendChild(progressElement);
}
/**
* Updates progress bars for all visible videos
*/
function updateVideoProgress() {
const videos = [...document.querySelectorAll(SELECTORS.VIDEO)].reverse();
const isReelPage = window.location.pathname.includes('reel');
videos.forEach(video => {
if (isElementVisibleInViewport(video, !isReelPage)) {
createOrUpdateProgressBar(video);
}
});
}
/**
* Pauses autoplay videos in the feed
*/
function pauseAutoplayVideos() {
const autoplayVideos = document.querySelectorAll(SELECTORS.REEL_VIDEO);
autoplayVideos?.forEach(video => {
if (!video.paused) {
video.pause();
}
});
}
/**
* Seeks the current video by a duration
* @param {number} duration - Duration in seconds (negative for backward)
*/
function seekCurrentVideo(duration) {
const video = getCurrentVideo();
if (video) {
video.currentTime = Math.max(0, Math.min(
video.currentTime + duration,
video.duration
));
}
}
/**
* Toggles play/pause for the current video
*/
function toggleVideoPlayback() {
const video = getCurrentVideo();
if (video) {
if (video.paused) {
video.play();
} else {
video.pause();
}
}
}
/**
* Seeks to a specific percentage of the video
* @param {number} percentage - Percentage (0-100)
*/
function seekToPercentage(percentage) {
const video = getCurrentVideo();
if (video) {
video.currentTime = video.duration * (percentage / 100);
}
}
// ==================== MEDIA OPENING FUNCTIONS ====================
/**
* Opens media based on the current page context
* @param {number} operation - The operation type (DOWNLOAD, OPEN, COPY)
*/
function openMedia(operation = OPERATIONS.OPEN) {
const pathname = window.location.pathname;
const href = window.location.href;
// Home feed
if (pathname === '/') {
openMediaFromFeed();
return;
}
// Post or Reel page
if (href.includes('/p/') || href.includes('/reel/')) {
openVideoFromPost();
return;
}
// Stories
if (href.includes('stories')) {
openImageFromStories();
return;
}
// Reels feed
if (href.includes('reels')) {
openVideoFromReels(operation);
return;
}
// Fallback
openImageFromHover() || openVideoFromReels(operation);
}
/**
* Opens media from the home feed
*/
function openMediaFromFeed() {
try {
const hoveredArticle = getLastHoveredElement()?.closest(SELECTORS.ARTICLE);
if (!hoveredArticle) return;
let image = hoveredArticle.querySelector('div>img[alt^="Photo by"]');
if(image)
{
openFromPointer();
return;
}
const postLinks = hoveredArticle.querySelectorAll(SELECTORS.POST_LINK);
const lastLink = [...postLinks].at(-1);
if (!lastLink) return;
const shortcode = lastLink.getAttribute('href').split('/').at(2);
const fallbackLink = `https://www.instagram.com/p/${shortcode}#downloadVideo`;
window.open(fallbackLink, '_blank');
} catch (error) {
console.error('Failed to open media from feed:', error);
}
}
let lastMouseX = 0;
let lastMouseY = 0;
// Track mouse position globally
document.addEventListener('mousemove', (e) => {
lastMouseX = e.clientX;
lastMouseY = e.clientY;
}, true);
function openFromPointer(){
// Get all elements at the last mouse position
const elements = document.elementsFromPoint(lastMouseX, lastMouseY);
if (!elements || elements.length === 0) {
console.log('No elements found at mouse position');
return;
}
// Search through elements to find media
for (const el of elements) {
let mediaUrl = null;
// Check for IMG elements
if (el.tagName === 'IMG') {
mediaUrl = el.src || el.dataset.src || el.dataset.lazySrc;
}
// Check for VIDEO elements
else if (el.tagName === 'VIDEO') {
if(!el.src.startsWith('blob:'))
{
mediaUrl = el.src || el.currentSrc || (el.querySelector('source')?.src);
}else {
let article = el.closest('article');
if(article)
{
let href = article.querySelector("a[role='link'][href*='/p']");
if(href) mediaUrl = href.replace('liked_by/','')+'#downloadVideo';
}
}
}
// Check for elements with background images
else if (el.style.backgroundImage) {
const bgMatch = el.style.backgroundImage.match(/url\(['"]?(.*?)['"]?\)/);
if (bgMatch) {
mediaUrl = bgMatch[1];
}
}
// Check for A tags linking to images/videos
else if (el.tagName === 'A') {
const href = el.href;
if (href && /\.(jpg|jpeg|png|gif|webp|mp4|webm|mov)(\?|$)/i.test(href)) {
mediaUrl = href;
}
}
// Check for common data attributes
else if (el.dataset.fullImage || el.dataset.fullSrc) {
mediaUrl = el.dataset.fullImage || el.dataset.fullSrc;
}
// If media found, open it
if (mediaUrl) {
console.log('Found media:', mediaUrl);
window.open(mediaUrl, '_blank');
return;
}
}
}
/**
* Opens video from a post page
*/
function openVideoFromPost() {
try {
const url = extractVideoUrlFromHtml(document.body.innerHTML);
if (url) {
window.open(url, '_blank');
} else {
throw new Error('Video URL not found');
}
} catch (error) {
window.open(window.location.href + `#downloadVideo`, '_blank');
console.error('Failed to open video from post:', error);
}
}
/**
* Opens image from stories
*/
function openImageFromStories() {
const hoveredElement = getLastHoveredElement();
const imageElement = hoveredElement?.querySelector('img');
const href = imageElement?.getAttribute('src');
if (href) {
window.open(href, '_blank');
}
}
/**
* Opens video from reels
* @param {number} operation - The operation type
*/
function openVideoFromReels(operation) {
handleMediaError(new Error('Reel video extraction'));
}
/**
* Opens image from hovered element
* @returns {boolean} Success status
*/
function openImageFromHover() {
try {
const hoveredElement = getLastHoveredElement();
const imageElement = hoveredElement?.querySelector('img');
if (!imageElement) return false;
const href = getBestSourceFromSrcset(imageElement);
if (href) {
window.open(href, '_blank');
return true;
}
} catch (error) {
console.error('Failed to open image from hover:', error);
}
return false;
}
// ==================== KEYBOARD HANDLER ====================
/**
* Checks if user is currently typing in an input field
* @returns {boolean} True if input is focused
*/
function isInputFocused() {
const activeElement = document.activeElement;
if (activeElement?.getAttribute('aria-label')?.includes('comment')) {
return true;
}
return document.querySelector('input:focus') !== null ||
document.querySelector('textarea:focus') !== null;
}
/**
* Handles keyboard shortcuts
* @param {KeyboardEvent} event - The keyboard event
*/
function handleKeyboardShortcut(event) {
if (isInputFocused()) return;
const key = event.key;
const keyNumber = parseInt(key);
// Number keys for seeking to percentage
if (keyNumber > 0 && keyNumber <= 9) {
seekToPercentage(keyNumber * 10);
return;
}
// Arrow keys and comma/period for seeking
if (KEYBOARD_SHORTCUTS.SEEK_BACKWARD.includes(key)) {
seekCurrentVideo(-CONFIG.SEEK_DURATION);
return;
}
if (KEYBOARD_SHORTCUTS.SEEK_FORWARD.includes(key)) {
seekCurrentVideo(CONFIG.SEEK_DURATION);
return;
}
// Space for play/pause
if (KEYBOARD_SHORTCUTS.PLAY_PAUSE.includes(key)) {
if (window.location.href.includes('stories')) return;
event.preventDefault();
toggleVideoPlayback();
return;
}
// 'R' key for opening images
if (key === 'r' && !event.shiftKey && !event.ctrlKey &&
!event.altKey && !event.metaKey) {
openImageFromHover();
return;
}
// 'E' key for opening media
if (key === 'e' && !event.ctrlKey) {
openMedia(OPERATIONS.OPEN);
return;
}
}
// ==================== HASH HANDLER ====================
/**
* Handles download video hash in URL
*/
function handleDownloadVideoHash() {
if (!window.location.hash.includes('downloadVideo')) return;
try {
const url = extractVideoUrlFromHtml(document.body.innerHTML);
if (url) {
GM_openInTab(url, { loadInBackground: false });
setTimeout(() => window.close(), 100);
}
} catch (error) {
console.error('Failed to handle download hash:', error);
}
}
// ==================== INITIALIZATION ====================
/**
* Initializes the userscript
*/
function initialize() {
// Handle download hash immediately
handleDownloadVideoHash();
// Set up periodic tasks
setInterval(() => {
if (document.visibilityState !== 'visible') return;
updateVideoProgress();
pauseAutoplayVideos();
}, CONFIG.PROGRESS_UPDATE_INTERVAL);
// Set up keyboard shortcuts
document.addEventListener('keydown', handleKeyboardShortcut, { passive: true });
// Register menu command
GM_registerMenuCommand('Open Media', () => {
openMedia(OPERATIONS.OPEN);
}, 'm');
console.log('Instagram Media Tools initialized');
}
// Start after a short delay to ensure page is ready
setTimeout(initialize, CONFIG.INIT_DELAY);
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment