Greasy Fork

来自缓存

Greasy Fork is available in English.

Instagram 媒体下载器 (高清)

以单个、JSON 或 ZIP 格式高清下载个人资料中的所有媒体(照片、视频、Reels)。

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         Instagram Media Downloader (HD)
// @name:tr      Instagram Medya İndirici (HD)
// @name:zh-CN   Instagram 媒体下载器 (高清)
// @name:fr      Instagram Téléchargeur de Médias (HD)
// @name:ru      Instagram Загрузчик медиа (HD)
// @name:ar      إنستغرام منزل الوسائط (HD)
// @namespace    https://tr-wp.com/
// @version      1.2.0
// @description  Download all media from a profile (Photos, Videos, Reels) in high quality as Single, JSON, or ZIP.
// @description:tr Profildeki tüm medyaları (Fotoğraf, Video, Reels) yüksek kalitede Tekli, JSON veya ZIP olarak indirir.
// @description:zh-CN 以单个、JSON 或 ZIP 格式高清下载个人资料中的所有媒体(照片、视频、Reels)。
// @description:fr Téléchargez tous les médias d'un profil (Photos, Vidéos, Reels) en haute qualité au format Solo, JSON ou ZIP.
// @description:ru Загружайте все медиафайлы из профиля (Фото, Видео, Reels) в высоком качестве в форматах Solo, JSON или ZIP.
// @description:ar قم بتنزيل جميع الوسائط من الملف الشخصي (صور، مقاطع فيديو، ريلز) بجودة عالية بتنسيق فردي أو JSON أو ZIP.
// @author       Yoka - tr-wp.com
// @match        https://www.instagram.com/*
// @exclude      https://www.instagram.com/reels/*
// @exclude      https://www.instagram.com/p/*
// @exclude      https://www.instagram.com/explore/*
// @grant        GM_xmlhttpRequest
// @require      https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2.0.5/FileSaver.min.js
// @run-at       document-idle
// @license      MIT
// @icon         https://www.google.com/s2/favicons?domain=instagram.com&sz=64
// ==/UserScript==

(function () {
    'use strict';

    var IG_APP_ID = '936619743392459';

    // --- Localization ---
    const i18n = {
        en: { media: "Media", json: "JSON", zip: "ZIP", loading: "Loading...", user: "User...", fetching: "Fetching ", zipPrep: "Preparing ZIP...", done: "Downloaded", error: "Error", noMedia: "No media found.", session: "Ensure you are logged in." },
        tr: { media: "Medya", json: "JSON", zip: "ZIP", loading: "Yükleniyor...", user: "Kullanıcı...", fetching: "Yükleniyor ", zipPrep: "ZIP hazırlanıyor...", done: "indirildi", error: "Hata", noMedia: "Hiç medya bulunamadı.", session: "Instagram oturumunun açık olduğundan emin olun." },
        zh: { media: "媒体", json: "JSON", zip: "ZIP", loading: "正在加载...", user: "用户...", fetching: "正在获取 ", zipPrep: "正在准备 ZIP...", done: "已下载", error: "错误", noMedia: "未找到媒体。", session: "确保您已登录。" },
        fr: { media: "Médias", json: "JSON", zip: "ZIP", loading: "Chargement...", user: "Utilisateur...", fetching: "Récupération ", zipPrep: "Préparation du ZIP...", done: "téléchargé", error: "Erreur", noMedia: "Aucun média trouvé.", session: "Assurez-vous d'être connecté." },
        ru: { media: "Медиа", json: "JSON", zip: "ZIP", loading: "Загрузка...", user: "Пользователь...", fetching: "Получение ", zipPrep: "Подготовка ZIP...", done: "скачано", error: "Ошибка", noMedia: "Медиа не найдено.", session: "Убедитесь, что вы вошли в систему." },
        ar: { media: "وسائط", json: "JSON", zip: "ZIP", loading: "جاري التحميل...", user: "مستخدم...", fetching: "جاري جلب ", zipPrep: "جاري تحضير ZIP...", done: "تم التنزيل", error: "خطأ", noMedia: "لم يتم العثور على وسائط.", session: "تأكد من تسجيل الدخول." }
    };

    const lang = i18n[navigator.language.split('-')[0]] || i18n.en;

    // ── Toast ─────────────────────────────────────────────────
    function toast(msg, type) {
        var el = document.createElement('div');
        var bg = type === 'err' ? '#c0392b' : type === 'ok' ? '#27ae60' : '#2c3e50';
        Object.assign(el.style, {
            position: 'fixed', bottom: '24px', right: '16px', zIndex: '2147483648',
            background: bg, color: '#fff', padding: '10px 16px',
            borderRadius: '8px', fontSize: '12px', fontFamily: 'sans-serif',
            maxWidth: '280px', lineHeight: '1.5', boxShadow: '0 4px 12px rgba(0,0,0,.4)',
            opacity: '0', transition: 'opacity .25s', whiteSpace: 'pre-line'
        });
        el.textContent = msg;
        document.body.appendChild(el);
        requestAnimationFrame(function () { el.style.opacity = '1'; });
        setTimeout(function () {
            el.style.opacity = '0';
            setTimeout(function () { el.remove(); }, 300);
        }, type === 'err' ? 5000 : 3000);
    }

    // ── API ───────────────────────────────────────────────────
    function apiGet(url) {
        return new Promise(function (resolve, reject) {
            GM_xmlhttpRequest({
                method: 'GET', url: url, timeout: 20000,
                headers: {
                    'x-ig-app-id': IG_APP_ID,
                    'Accept': 'application/json',
                    'Accept-Language': 'tr-TR,tr;q=0.9',
                    'Referer': 'https://www.instagram.com/',
                    'X-Requested-With': 'XMLHttpRequest'
                },
                onload: function (r) {
                    if (r.status < 200 || r.status >= 300) return reject(new Error('HTTP ' + r.status));
                    try { resolve(JSON.parse(r.responseText)); }
                    catch (e) { reject(new Error('JSON parse error')); }
                },
                onerror: function () { reject(new Error(lang.error)); },
                ontimeout: function () { reject(new Error('Timeout')); }
            });
        });
    }

    function getUserId(username) {
        return apiGet('https://www.instagram.com/api/v1/users/web_profile_info/?username=' + username)
            .then(function (d) {
                var pk = d && d.data && d.data.user && d.data.user.id;
                if (!pk) throw new Error(lang.user);
                return pk;
            });
    }

    function fetchAllPosts(userId, btnId) {
        var posts = [], cursor = null;
        function nextPage() {
            var url = 'https://www.instagram.com/api/v1/feed/user/' + userId + '/?count=12';
            if (cursor) url += '&max_id=' + encodeURIComponent(cursor);
            return apiGet(url).then(function (data) {
                var items = data.items || [];
                items.forEach(function (item) {
                    var isVideo = item.media_type === 2;
                    var sc = item.code || item.shortcode;
                    var mediaUrl;
                    if (isVideo) {
                        mediaUrl = item.video_url ||
                            (item.video_versions && item.video_versions[0] && item.video_versions[0].url);
                    }
                    if (!mediaUrl) {
                        var cands = item.image_versions2 && item.image_versions2.candidates;
                        if (cands && cands[0]) mediaUrl = cands[0].url;
                    }
                    if (mediaUrl) posts.push({ shortcode: sc, type: isVideo ? 'video' : 'photo', url: mediaUrl });
                });
                setBtn(btnId, lang.fetching + posts.length);
                cursor = data.next_max_id || null;
                if (data.more_available && cursor && posts.length < 500)
                    return sleep(800).then(nextPage);
                return posts;
            });
        }
        return nextPage();
    }

    // ── CRC-32 ───────────────────────────────────────────────
    var CRC_TABLE = (function () {
        var t = new Uint32Array(256);
        for (var i = 0; i < 256; i++) {
            var c = i;
            for (var j = 0; j < 8; j++) c = (c & 1) ? (0xEDB88320 ^ (c >>> 1)) : (c >>> 1);
            t[i] = c;
        }
        return t;
    })();
    function crc32(u8) {
        var crc = 0xFFFFFFFF;
        for (var i = 0; i < u8.length; i++) crc = (crc >>> 8) ^ CRC_TABLE[(crc ^ u8[i]) & 0xFF];
        return (crc ^ 0xFFFFFFFF) >>> 0;
    }

    // ── Manual ZIP (STORE) ───────────────────────────────────
    function buildZip(files) {
        var enc = new TextEncoder();
        var locals = [], central = [], offsets = [], pos = 0;
        var D = 0x5745, T = 0x0000;
        for (var fi = 0; fi < files.length; fi++) {
            var nb=enc.encode(files[fi].name), data=files[fi].data, crc=crc32(data), sz=data.length;
            var lh=new Uint8Array(30+nb.length), lv=new DataView(lh.buffer);
            lv.setUint32(0,0x04034b50,true);lv.setUint16(4,20,true);lv.setUint16(6,0,true);
            lv.setUint16(8,0,true);lv.setUint16(10,T,true);lv.setUint16(12,D,true);
            lv.setUint32(14,crc,true);lv.setUint32(18,sz,true);lv.setUint32(22,sz,true);
            lv.setUint16(26,nb.length,true);lv.setUint16(28,0,true);lh.set(nb,30);
            offsets.push(pos);locals.push(lh,data);pos+=lh.length+data.length;
            var ce=new Uint8Array(46+nb.length), cv=new DataView(ce.buffer);
            cv.setUint32(0,0x02014b50,true);cv.setUint16(4,20,true);cv.setUint16(6,20,true);
            cv.setUint16(8,0,true);cv.setUint16(10,0,true);cv.setUint16(12,T,true);cv.setUint16(14,D,true);
            cv.setUint32(16,crc,true);cv.setUint32(20,sz,true);cv.setUint32(24,sz,true);
            cv.setUint16(28,nb.length,true);cv.setUint16(30,0,true);cv.setUint16(32,0,true);
            cv.setUint16(34,0,true);cv.setUint16(36,0,true);cv.setUint32(38,0,true);
            cv.setUint32(42,offsets[fi],true);ce.set(nb,46);central.push(ce);
        }
        var cdOff=pos, cdSz=central.reduce(function(s,c){return s+c.length;},0);
        var eocd=new Uint8Array(22), ev=new DataView(eocd.buffer);
        ev.setUint32(0,0x06054b50,true);ev.setUint16(4,0,true);ev.setUint16(6,0,true);
        ev.setUint16(8,files.length,true);ev.setUint16(10,files.length,true);
        ev.setUint32(12,cdSz,true);ev.setUint32(16,cdOff,true);ev.setUint16(20,0,true);
        var all=locals.concat(central).concat([eocd]);
        var tot=all.reduce(function(s,a){return s+a.length;},0);
        var out=new Uint8Array(tot), cur=0;
        for(var i=0;i<all.length;i++){out.set(all[i],cur);cur+=all[i].length;}
        return out;
    }

    function strToU8(str) {
        return new TextEncoder().encode(str);
    }

    // ── UI ───────────────────────────────────────────────────
    function buildUI() {
        if (document.getElementById('igmt-bar')) return;
        var bar = document.createElement('div'); bar.id = 'igmt-bar';
        Object.assign(bar.style, {
            position: 'fixed', top: '60px', right: '16px', zIndex: '2147483647',
            display: 'flex', flexDirection: 'column', alignItems: 'stretch', gap: '5px',
            background: 'rgba(15,15,15,0.96)', padding: '8px',
            borderRadius: '10px', border: '1px solid #3a3a3a',
            backdropFilter: 'blur(6px)', fontFamily: 'sans-serif'
        });

        [{id:'igmt-media',text:lang.media,bg:'#d62976'},
         {id:'igmt-json', text:lang.json, bg:'#0095f6'},
         {id:'igmt-zip',  text:lang.zip,  bg:'#28a745'}
        ].forEach(function (b) {
            var btn = document.createElement('button');
            btn.id = b.id; btn.textContent = b.text; btn.dataset.def = b.text;
            Object.assign(btn.style, {
                padding: '6px 14px', background: b.bg, color: '#fff',
                border: 'none', borderRadius: '6px', cursor: 'pointer',
                fontSize: '11px', fontWeight: '700', width: '100%',
                textAlign: 'center'
            });
            btn.addEventListener('click', function () { run(b.id); });
            bar.appendChild(btn);
        });

        var prog = document.createElement('div'); prog.id = 'igmt-prog';
        Object.assign(prog.style, { height: '3px', background: '#333', borderRadius: '2px', marginTop: '2px', overflow: 'hidden', display: 'none' });
        var fill = document.createElement('div'); fill.id = 'igmt-fill';
        Object.assign(fill.style, { height: '100%', width: '0%', background: '#0095f6', transition: 'width .3s', borderRadius: '2px' });
        prog.appendChild(fill); bar.appendChild(prog);

        var st = document.createElement('div'); st.id = 'igmt-status';
        Object.assign(st.style, { color: '#666', fontSize: '9px', textAlign: 'center', marginTop: '2px', maxWidth: '100px', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' });
        bar.appendChild(st);
        document.body.appendChild(bar);
    }

    function setBtn(id,t){var e=document.getElementById(id);if(e)e.textContent=t;}
    function setStatus(t){var e=document.getElementById('igmt-status');if(e)e.textContent=t;}
    function setProgress(p){
        var pg=document.getElementById('igmt-prog'),f=document.getElementById('igmt-fill');
        if(!pg||!f)return;
        if(p===null){pg.style.display='none';f.style.width='0%';return;}
        pg.style.display='block';f.style.width=Math.round(p)+'%';
    }
    function lockAll(on){
        ['igmt-media','igmt-json','igmt-zip'].forEach(function(id){
            var e=document.getElementById(id);if(!e)return;
            e.disabled=on;e.style.opacity=on?'0.5':'1';e.style.cursor=on?'not-allowed':'pointer';
        });
    }

    // ── İndirme ──────────────────────────────────────────────
    function fetchBase64(url) {
        return new Promise(function(resolve,reject){
            GM_xmlhttpRequest({
                method:'GET',url:url,responseType:'blob',timeout:30000,
                headers:{'Referer':'https://www.instagram.com/','Accept':'image/webp,image/*,video/*,*/*'},
                onload:function(r){
                    if(r.status<200||r.status>=300) return reject(new Error('HTTP '+r.status));
                    function read(blob){
                        var fr=new FileReader();
                        fr.onload=function(){resolve(fr.result.split(',')[1]);};
                        fr.onerror=function(){reject(new Error('FileReader error'));};
                        fr.readAsDataURL(blob);
                    }
                    var resp=r.response;
                    if(resp&&typeof resp.then==='function') resp.then(read,reject); else read(resp);
                },
                onerror:function(){reject(new Error('Network Error'));},
                ontimeout:function(){reject(new Error('Timeout'));}
            });
        });
    }

    function b64ToU8(b64){
        var raw=atob(b64),u8=new Uint8Array(raw.length);
        for(var i=0;i<raw.length;i++) u8[i]=raw.charCodeAt(i);
        return u8;
    }

    // ── Ana Akış ─────────────────────────────────────────────
    async function run(btnId) {
        if (window._igmtBusy) return;
        window._igmtBusy = true;
        lockAll(true); setProgress(0); setStatus('');
        var defLabel = document.getElementById(btnId).dataset.def;
        var username = window.location.pathname.replace(/\//g,'').trim();

        try {
            if (!username) throw new Error('Username not found');

            setBtn(btnId, lang.user);
            var userId = await getUserId(username);

            setBtn(btnId, lang.loading);
            var posts = await fetchAllPosts(userId, btnId);
            if (!posts.length) { toast(lang.noMedia, 'err'); return; }

            // JSON modu
            if (btnId === 'igmt-json') {
                saveAs(new Blob([JSON.stringify(posts,null,2)],{type:'application/json'}), username+'.json');
                toast('✓ ' + posts.length + ' ' + lang.done, 'ok');
                return;
            }

            var isZip = btnId === 'igmt-zip';
            var zipFiles = [], errors = 0;
            var videoTotal = posts.filter(function(p){return p.type==='video';}).length;
            var videoHit = 0;

            for (var i = 0; i < posts.length; i++) {
                var post = posts[i];
                var idx  = String(i+1).padStart(4,'0');
                var isVideo = post.type === 'video';
                var ext  = isVideo ? 'mp4' : 'jpg';
                var mime = isVideo ? 'video/mp4' : 'image/jpeg';
                var name = username + '_' + idx + '.' + ext;

                if (isVideo) videoHit++;
                setBtn(btnId, (i+1)+'/'+posts.length);
                setStatus((isVideo?'▶ ':'🖼 ')+name);
                setProgress(Math.round(((i+1)/posts.length)*100));

                try {
                    var b64 = await fetchBase64(post.url);
                    var u8  = b64ToU8(b64);
                    if (isZip) { zipFiles.push({name:name, data:u8}); }
                    else { saveAs(new Blob([u8],{type:mime}), name); await sleep(150); }
                } catch(e) {
                    console.warn('[IGMT] Skip ('+(i+1)+'): '+e.message);
                    errors++;
                }
            }

            if (isZip) {
                var jsonU8 = strToU8(JSON.stringify(posts, null, 2));
                zipFiles.push({ name: username + '.json', data: jsonU8 });

                setBtn(btnId, lang.zipPrep); setStatus(''); setProgress(99);
                var zipU8 = buildZip(zipFiles);
                saveAs(new Blob([zipU8],{type:'application/zip'}), username+'.zip');
            }

            var msg = (errors ? '✓ '+(posts.length-errors)+' success  ✗ '+errors+' error\n' : '✓ '+posts.length+' ' + lang.done + '\n')
                    + '📹 '+videoHit+'/'+videoTotal+' video';
            toast(msg, errors ? 'err' : 'ok');

        } catch(err) {
            console.error('[IGMT] Error:',err);
            toast('✗ ' + err.message + '\n' + lang.session, 'err');
        } finally {
            setBtn(btnId,defLabel); setStatus(''); setProgress(null);
            lockAll(false); window._igmtBusy = false;
        }
    }

    function sleep(ms){return new Promise(function(r){setTimeout(r,ms);});}
    setTimeout(buildUI, 1000);
})();