instagram.mjs

Instagram downloader with complete metadata.

#downloader#tools#media
16
27 Des 2025, 04:22
RawEdit
javascript0 lines
/***
  @ Base: https://www.instagram.com/
  @ Author: Shannz
  @ Note: Instagram downloader with complete metadata.
***/

import axios from 'axios';
import * as cheerio from 'cheerio';
import { XMLParser } from 'fast-xml-parser';

export async function instagram(url) {
    if (!url) return { status: false, error: 'URL tidak valid atau kosong.' };

    try {
        console.log(`Please Wait...`);

        const response = await axios.get(url, {
            headers: {
                'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36',
                'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
                'cache-control': 'max-age=0',
                'dpr': '2',
                'viewport-width': '980',
                'sec-ch-ua': '"Chromium";v="136", "Google Chrome";v="136", "Not.A/Brand";v="99"',
                'sec-ch-ua-mobile': '?1',
                'sec-ch-ua-platform': '"Android"',
                'sec-ch-ua-platform-version': '"15.0.0"',
                'sec-ch-ua-model': '"25028RN03A"',
                'sec-ch-ua-full-version-list': '"Chromium";v="136.0.7103.125", "Google Chrome";v="136.0.7103.125", "Not.A/Brand";v="99.0.0.0"',
                'sec-ch-prefers-color-scheme': 'light',
                'dnt': '1',
                'upgrade-insecure-requests': '1',
                'sec-fetch-site': 'same-origin',
                'sec-fetch-mode': 'navigate',
                'sec-fetch-user': '?1',
                'sec-fetch-dest': 'document',
                'accept-language': 'id-ID,id;q=0.9,en-US;q=0.8,en;q=0.7',
                'priority': 'u=0, i'
            },
            timeout: 10000
        });

        const $ = cheerio.load(response.data);
        let scriptJson = null;

        $('script[type="application/json"]').each((_, el) => {
            const content = $(el).html();
            if (content && content.includes('xdt_api__v1__media__shortcode__web_info')) {
                try {
                    scriptJson = JSON.parse(content);
                } catch (parseError) {
                    console.error('JSON Parse Error:', parseError.message);
                }
            }
        });

        if (!scriptJson) throw new Error('Data script tidak ditemukan (Mungkin IP Blocked atau bukan Reels).');

        const item = scriptJson.require?.[0]?.[3]?.[0]?.__bbox?.require?.[0]?.[3]?.[1]?.__bbox?.result?.data?.xdt_api__v1__media__shortcode__web_info?.items?.[0];

        if (!item) throw new Error('Struct item tidak ditemukan dalam JSON.');

        const dashXml = item.video_dash_manifest;
        if (!dashXml) throw new Error('Manifest XML tidak ditemukan.');

        const parser = new XMLParser({ ignoreAttributes: false });
        let manifest;
        try {
            manifest = parser.parse(dashXml);
        } catch (xmlError) {
            throw new Error(`Gagal parsing DASH manifest: ${xmlError.message}`);
        }

        const period = manifest.MPD?.Period;
        if (!period) throw new Error('Tag Period tidak ditemukan di XML.');

        const adaptationSets = Array.isArray(period.AdaptationSet) ? period.AdaptationSet : [period.AdaptationSet];
        let videoTracks = [];
        let audioTracks = [];

        adaptationSets.forEach((set) => {
            if (!set) return;

            const isVideo = set['@_contentType'] === 'video';
            const isAudio = set['@_contentType'] === 'audio';
            const representations = Array.isArray(set.Representation) ? set.Representation : [set.Representation];

            representations.forEach((rep) => {
                if (!rep) return;

                const track = {
                    url: rep.BaseURL,
                    bandwidth: parseInt(rep['@_bandwidth']) || 0,
                    codecs: rep['@_codecs'] || '',
                    mimeType: rep['@_mimeType'] || '',
                };

                if (isVideo) {
                    videoTracks.push({
                        ...track,
                        resolution: `${rep['@_width']}x${rep['@_height']}`,
                        qualityLabel: rep['@_FBQualityLabel'] || ''
                    });
                } else if (isAudio) {
                    audioTracks.push(track);
                }
            });
        });

        videoTracks.sort((a, b) => b.bandwidth - a.bandwidth);

        const finalResult = {
            metadata: {
                id: item.id,
                code: item.code,
                caption: item.caption?.text || '',
                createTime: new Date(item.taken_at * 1000).toLocaleString(),
            },
            author: {
                id: item.user?.pk,
                username: item.user?.username || 'N/A',
                fullName: item.user?.full_name || '',
                profilePic: item.user?.hd_profile_pic_url_info?.url || '',
                verified: item.user?.is_verified
            },
            media: {
                thumbnails: (item.image_versions2?.candidates || []).map(img => ({
                    url: img.url,
                    resolution: `${img.width}x${img.height}`
                })),
                videos: videoTracks,
                audios: audioTracks
            }
        };

        return {
            status: true,
            result: finalResult
        };

    } catch (error) {
        console.error('Error Main Process:', error.message);
        return { status: false, error: error.message };
    }
}