ultra-igdl 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ultra-igdl contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,594 @@
1
+ # ultra-igdl
2
+
3
+ **Production-grade Instagram media extractor for Node.js 20+**
4
+
5
+ Fetch direct CDN URLs for reels, posts, carousels, stories, and highlights. Built from scratch with a multi-layer HTML/JSON parser, optional logged-in session API, connection pooling, LRU cache, and a CLI — **no wrapper around other Instagram downloader packages**.
6
+
7
+ ---
8
+
9
+ ## Table of contents
10
+
11
+ 1. [Who is this for?](#who-is-this-for)
12
+ 2. [Requirements](#requirements)
13
+ 3. [Installation](#installation)
14
+ 4. [Quick start (beginner)](#quick-start-beginner)
15
+ 5. [Instagram session (important)](#instagram-session-important)
16
+ 6. [CLI guide](#cli-guide)
17
+ 7. [Supported URLs](#supported-urls)
18
+ 8. [Response format](#response-format)
19
+ 9. [Content types explained](#content-types-explained)
20
+ 10. [JavaScript / TypeScript API](#javascript--typescript-api)
21
+ 11. [Configuration options](#configuration-options)
22
+ 12. [Pro features](#pro-features)
23
+ 13. [Error codes & troubleshooting](#error-codes--troubleshooting)
24
+ 14. [Examples in this repo](#examples-in-this-repo)
25
+ 15. [Architecture](#architecture)
26
+ 16. [Development](#development)
27
+ 17. [Publishing to npm](#publishing-to-npm)
28
+ 18. [Legal & disclaimer](#legal--disclaimer)
29
+ 19. [License](#license)
30
+
31
+ ---
32
+
33
+ ## Who is this for?
34
+
35
+ | Level | You can… |
36
+ |--------|-----------|
37
+ | **Beginner** | Install with npm, run `npx ultra-igdl <url> --json`, paste a browser cookie for full carousels |
38
+ | **Intermediate** | Use `ultraigdl` in Express/Fastify bots, batch URLs, download files with `--download` |
39
+ | **Pro** | Tune cache/Redis, `fastMode` + `prefetch`, `maxConcurrency`, integrate `DownloaderCore` |
40
+
41
+ ---
42
+
43
+ ## Requirements
44
+
45
+ - **Node.js** 20.18.1+ or 22 (LTS recommended)
46
+ - **npm** 9+ (or pnpm/yarn)
47
+ - Public Instagram URLs (no login required for basic post/reel preview)
48
+ - **Optional:** Instagram `sessionid` cookie for carousels (all slides), reel MP4, stories, highlights
49
+
50
+ ---
51
+
52
+ ## Installation
53
+
54
+ ```bash
55
+ npm install ultra-igdl
56
+ ```
57
+
58
+ CLI only (no install into project):
59
+
60
+ ```bash
61
+ npx ultra-igdl --help
62
+ ```
63
+
64
+ ---
65
+
66
+ ## Quick start (beginner)
67
+
68
+ ### 1. Programmatic (ESM)
69
+
70
+ ```js
71
+ import { ultraigdl } from "ultra-igdl";
72
+
73
+ const ig = new ultraigdl();
74
+ const result = await ig.download("https://www.instagram.com/reel/SHORTCODE/");
75
+
76
+ if (result.code === 200) {
77
+ console.log("User:", result.username);
78
+ console.log("Caption:", result.caption);
79
+ console.log("Files:", result.media.length);
80
+ result.media.forEach((m, i) => console.log(i + 1, m.type, m.url));
81
+ } else {
82
+ console.error(result.code, result.message);
83
+ }
84
+ ```
85
+
86
+ ### 2. CommonJS
87
+
88
+ ```js
89
+ const { ultraigdl } = require("ultra-igdl");
90
+
91
+ (async () => {
92
+ const ig = new ultraigdl();
93
+ const result = await ig.download("https://www.instagram.com/p/SHORTCODE/");
94
+ console.log(result);
95
+ })();
96
+ ```
97
+
98
+ ### 3. CLI (JSON)
99
+
100
+ ```bash
101
+ npx ultra-igdl "https://www.instagram.com/p/SHORTCODE/" --json
102
+ ```
103
+
104
+ On **Windows PowerShell**, always wrap URLs in **double quotes** (see [CLI guide](#cli-guide)).
105
+
106
+ ---
107
+
108
+ ## Instagram session (important)
109
+
110
+ Instagram limits what **logged-out** visitors see:
111
+
112
+ | Feature | Without session | With session (`sessionId` / `cookies`) |
113
+ |---------|-----------------|----------------------------------------|
114
+ | Single post image | Usually yes | Yes (full resolution) |
115
+ | **Carousel (2+ photos)** | Often **1 slide only** | **All slides** |
116
+ | **Reel MP4** | Often thumbnail only | **Video URL** |
117
+ | **Story** | Usually fails / preview | **Media URL** |
118
+ | **Highlight** | Limited | **Video** when available |
119
+
120
+ ### How to get cookies (browser)
121
+
122
+ 1. Log in to [instagram.com](https://www.instagram.com) in Chrome/Edge/Firefox.
123
+ 2. Open DevTools → **Application** → **Cookies** → `https://www.instagram.com`.
124
+ 3. Copy:
125
+ - `sessionid`
126
+ - `csrftoken`
127
+ - `ds_user_id`
128
+
129
+ ### Option A — full cookie string (recommended)
130
+
131
+ ```js
132
+ const ig = new ultraigdl({
133
+ cookies:
134
+ "sessionid=YOUR_ID; csrftoken=YOUR_CSRF; ds_user_id=YOUR_USER_ID",
135
+ });
136
+ ```
137
+
138
+ ### Option B — session id only
139
+
140
+ ```js
141
+ const ig = new ultraigdl({
142
+ sessionId: "YOUR_SESSIONID_VALUE",
143
+ });
144
+ ```
145
+
146
+ ### Environment variables (CLI)
147
+
148
+ The CLI auto-loads `.env` from the current directory or `../ultra-igdl-live-test/.env`.
149
+
150
+ ```bash
151
+ # Linux / macOS
152
+ export INSTAGRAM_COOKIES="sessionid=...; csrftoken=...; ds_user_id=..."
153
+ npx ultra-igdl "https://www.instagram.com/p/SHORTCODE/" --json
154
+ ```
155
+
156
+ ```powershell
157
+ # Windows PowerShell — use TWO lines, or semicolon before npx
158
+ $env:INSTAGRAM_COOKIES = "sessionid=...; csrftoken=...; ds_user_id=..."
159
+ npx ultra-igdl "https://www.instagram.com/p/SHORTCODE/" --json
160
+ ```
161
+
162
+ **Never commit real cookies to git.** Add `.env` to `.gitignore` (already included).
163
+
164
+ ---
165
+
166
+ ## CLI guide
167
+
168
+ ```bash
169
+ npx ultra-igdl <url> [options]
170
+ npx ultra-igdl urls.txt # one URL per line
171
+ ```
172
+
173
+ | Flag | Short | Description |
174
+ |------|-------|-------------|
175
+ | `--json` | `-j` | Print full API response as JSON |
176
+ | `--download` | `-d` | Save media files under `--output` |
177
+ | `--output <dir>` | `-o` | Download folder (default: `./downloads`) |
178
+ | `--verbose` | `-v` | Debug logging |
179
+ | `--help` | `-h` | Show help |
180
+
181
+ ### PowerShell rules
182
+
183
+ 1. Wrap URLs in `"quotes"` when the link contains `&` (e.g. `?igsh=...&...`).
184
+ 2. Set env vars on **line 1**, run `npx` on **line 2**, **or** use `;` between them:
185
+
186
+ ```powershell
187
+ $env:INSTAGRAM_COOKIES = "sessionid=...; csrftoken=...; ds_user_id=..."; npx ultra-igdl "https://www.instagram.com/p/ABC/" --json
188
+ ```
189
+
190
+ ### CLI examples
191
+
192
+ ```bash
193
+ # Human-readable summary
194
+ npx ultra-igdl "https://www.instagram.com/reel/ABC123/"
195
+
196
+ # JSON for scripts
197
+ npx ultra-igdl "https://www.instagram.com/p/ABC123/" --json
198
+
199
+ # Download all carousel images
200
+ npx ultra-igdl "https://www.instagram.com/p/ABC123/" --download -o ./downloads
201
+
202
+ # Batch file (urls.txt)
203
+ npx ultra-igdl urls.txt --json
204
+ ```
205
+
206
+ ---
207
+
208
+ ## Supported URLs
209
+
210
+ | Type | Example pattern |
211
+ |------|-----------------|
212
+ | Post | `https://www.instagram.com/p/{shortcode}/` |
213
+ | Reel | `https://www.instagram.com/reel/{shortcode}/` |
214
+ | IGTV | `https://www.instagram.com/tv/{shortcode}/` |
215
+ | Story | `https://www.instagram.com/stories/{username}/{storyId}/` |
216
+ | Highlight (path) | `https://www.instagram.com/stories/highlights/{id}/` |
217
+ | Highlight (share) | `https://www.instagram.com/s/{token}?story_media_id=...` |
218
+
219
+ Validate before download:
220
+
221
+ ```js
222
+ const { valid, type, normalized } = await ig.validate(url);
223
+ ```
224
+
225
+ ---
226
+
227
+ ## Response format
228
+
229
+ ### Success (`code: 200`)
230
+
231
+ ```json
232
+ {
233
+ "code": 200,
234
+ "meta": { "extractor": "ultra-igdl", "version": "1.0.0" },
235
+ "media": [
236
+ {
237
+ "type": "image",
238
+ "url": "https://...cdninstagram.../....jpg",
239
+ "width": 1440,
240
+ "height": 1800
241
+ },
242
+ {
243
+ "type": "video",
244
+ "url": "https://...mp4",
245
+ "thumbnail": "https://...jpg",
246
+ "width": 1080,
247
+ "height": 1920,
248
+ "duration": 24
249
+ }
250
+ ],
251
+ "caption": "Post caption as a single clean line for posts",
252
+ "username": "creator",
253
+ "engagement": {
254
+ "likes": 1200,
255
+ "comments": 45
256
+ },
257
+ "tags": ["carousel"]
258
+ }
259
+ ```
260
+
261
+ ### Media object
262
+
263
+ | Field | Type | Description |
264
+ |-------|------|-------------|
265
+ | `type` | `"image"` \| `"video"` | Primary media type |
266
+ | `url` | `string` | Direct CDN URL (signed; do not edit query params) |
267
+ | `thumbnail` | `string?` | Poster frame for video |
268
+ | `width` / `height` | `number?` | Pixel dimensions when known |
269
+ | `duration` | `number?` | Video length in seconds |
270
+
271
+ ### Tags (`tags` array)
272
+
273
+ | Tag | Meaning |
274
+ |-----|---------|
275
+ | `carousel` | Multi-slide post; `media.length` ≥ 2 |
276
+ | `partial_carousel` | Carousel detected but only one slide returned |
277
+ | `session_recommended` | Add `sessionId` / `cookies` for full carousel |
278
+ | `likes_hidden` | Creator hid like counts |
279
+ | `comments_hidden` | Comments disabled or hidden |
280
+ | `engagement_hidden` | Both likes and comments hidden |
281
+
282
+ ### Error (`code` ≠ 200)
283
+
284
+ ```json
285
+ {
286
+ "code": 404,
287
+ "message": "Media not found",
288
+ "meta": { "extractor": "ultra-igdl", "version": "1.0.0" }
289
+ }
290
+ ```
291
+
292
+ Some responses include `retryAfterMs` when using `fastMode` / `responseBudgetMs` (background fetch still running).
293
+
294
+ ---
295
+
296
+ ## Content types explained
297
+
298
+ ### Posts (single image)
299
+
300
+ ```js
301
+ const result = await ig.download("https://www.instagram.com/p/SHORTCODE/");
302
+ // result.media.length === 1 typically
303
+ ```
304
+
305
+ ### Carousels (2+ photos)
306
+
307
+ - **Auto-detected** from any `/p/` URL — no special URL or env var.
308
+ - Without session: often 1 image + tags `partial_carousel`, `session_recommended`.
309
+ - With session: all slides, tag `carousel`.
310
+
311
+ ```js
312
+ const ig = new ultraigdl({ cookies: process.env.INSTAGRAM_COOKIES });
313
+ const result = await ig.download("https://www.instagram.com/p/SHORTCODE/");
314
+ console.log(result.media.length); // e.g. 4
315
+ console.log(result.tags); // ["carousel"]
316
+ ```
317
+
318
+ ### Reels
319
+
320
+ ```js
321
+ const ig = new ultraigdl({ sessionId: process.env.INSTAGRAM_SESSION_ID });
322
+ const result = await ig.download("https://www.instagram.com/reel/SHORTCODE/");
323
+ const video = result.media.find((m) => m.type === "video");
324
+ ```
325
+
326
+ ### Stories
327
+
328
+ Requires session. Story must still be live (not expired).
329
+
330
+ ```js
331
+ const result = await ig.download(
332
+ "https://www.instagram.com/stories/username/1234567890/"
333
+ );
334
+ ```
335
+
336
+ ### Highlights
337
+
338
+ Works with highlight URLs or `/s/` share links; session improves reliability.
339
+
340
+ ---
341
+
342
+ ## JavaScript / TypeScript API
343
+
344
+ ```ts
345
+ import { ultraigdl, type ApiResponse, type DownloadResponse } from "ultra-igdl";
346
+
347
+ const ig = new ultraigdl({ cache: true, retries: 2 });
348
+ ```
349
+
350
+ | Method | Returns | Description |
351
+ |--------|---------|-------------|
352
+ | `download(url)` | `Promise<ApiResponse>` | Full extraction (main method) |
353
+ | `info(url)` | `Promise<ApiResponse>` | Alias of `download` |
354
+ | `validate(url)` | `Promise<{ valid, type?, normalized? }>` | URL check + normalization |
355
+ | `media(url)` | `Promise<Media[] \| ErrorResponse>` | Media array only |
356
+ | `batch(urls)` | `Promise<BatchResult[]>` | Parallel downloads with per-URL timing |
357
+ | `prefetch(url)` | `Promise<ApiResponse>` | Warm cache for `fastMode` |
358
+ | `health()` | `Promise<HealthStatus>` | Cache stats, pool, version |
359
+ | `clearCache()` | `void` | Clear in-memory LRU cache |
360
+
361
+ ### TypeScript
362
+
363
+ Types are shipped in `dist/index.d.ts`. Narrow success responses:
364
+
365
+ ```ts
366
+ const result = await ig.download(url);
367
+ if (result.code === 200) {
368
+ const data = result as DownloadResponse;
369
+ data.media.forEach((m) => { /* ... */ });
370
+ }
371
+ ```
372
+
373
+ ### Helpers (also exported)
374
+
375
+ ```ts
376
+ import { validateUrl, parseInstagramUrl, isInstagramUrl } from "ultra-igdl";
377
+ ```
378
+
379
+ ---
380
+
381
+ ## Configuration options
382
+
383
+ ```ts
384
+ const ig = new ultraigdl({
385
+ // Cache
386
+ cache: true, // default: true
387
+ cacheTtlMs: 300_000, // 5 min fresh TTL
388
+ staleCacheTtlMs: 86_400_000, // 24h stale-while-revalidate
389
+ cacheMaxSize: 500,
390
+
391
+ // Network
392
+ maxConcurrency: 100,
393
+ timeoutMs: 15_000,
394
+ retries: 3,
395
+ userAgentRotation: true,
396
+
397
+ // Session
398
+ sessionId: "...",
399
+ cookies: "sessionid=...; csrftoken=...; ds_user_id=...",
400
+
401
+ // Low-latency mode (bots that reply in <500ms)
402
+ fastMode: true, // sets responseBudgetMs: 500, retries: 0
403
+ responseBudgetMs: 800,
404
+
405
+ // Optional Redis (implement RedisAdapter interface)
406
+ redis: myRedisAdapter,
407
+
408
+ verbose: false,
409
+ });
410
+ ```
411
+
412
+ ---
413
+
414
+ ## Pro features
415
+
416
+ ### Batch processing
417
+
418
+ ```ts
419
+ const results = await ig.batch([
420
+ "https://www.instagram.com/p/A/",
421
+ "https://www.instagram.com/reel/B/",
422
+ ]);
423
+ for (const { url, result, durationMs } of results) {
424
+ console.log(url, result.code, `${durationMs}ms`);
425
+ }
426
+ ```
427
+
428
+ ### Fast mode + prefetch (Telegram/Discord bots)
429
+
430
+ ```ts
431
+ const ig = new ultraigdl({ fastMode: true, cookies: "..." });
432
+
433
+ // Warm extraction while user types
434
+ await ig.prefetch(url);
435
+
436
+ // Often returns from cache within budget
437
+ let result = await ig.download(url);
438
+ if (result.code === 503 && result.retryAfterMs) {
439
+ await new Promise((r) => setTimeout(r, result.retryAfterMs));
440
+ result = await ig.download(url);
441
+ }
442
+ ```
443
+
444
+ ### Redis cache adapter
445
+
446
+ ```ts
447
+ import { ultraigdl, type RedisAdapter } from "ultra-igdl";
448
+
449
+ const redis: RedisAdapter = {
450
+ async get(key) { /* return string | null */ },
451
+ async set(key, value, ttlMs) { /* ... */ },
452
+ };
453
+
454
+ const ig = new ultraigdl({ redis });
455
+ ```
456
+
457
+ ### Download files to disk (library)
458
+
459
+ Use your own `fetch` on `media[].url`, or the CLI `--download` flag (uses built-in file downloader).
460
+
461
+ ---
462
+
463
+ ## Error codes & troubleshooting
464
+
465
+ | Code | Typical cause | What to do |
466
+ |------|---------------|------------|
467
+ | **400** | Invalid URL | Use `validate()`; check link format |
468
+ | **403** | Private account | Cannot extract without access |
469
+ | **404** | Deleted / wrong id / expired story | Verify URL in browser |
470
+ | **429** | Rate limited | Slow down; reduce concurrency; wait |
471
+ | **500** | Parse/network failure | Retry; update package; report issue |
472
+ | **503** | `fastMode` budget exceeded | Retry after `retryAfterMs` or disable fast mode |
473
+ | **504** | Timeout | Increase `timeoutMs` |
474
+
475
+ | Symptom | Fix |
476
+ |---------|-----|
477
+ | Carousel returns 1 image | Set `cookies` or `sessionId` |
478
+ | Reel has no MP4 | Add session cookie |
479
+ | CLI `Unexpected token 'npx'` | Use `;` or two lines in PowerShell |
480
+ | `Invalid or unexpected token` on CLI | Run `npm run build`; use published version |
481
+ | Caption has weird dots/lines | Post captions are flattened to one line by design |
482
+ | 403 on CDN URL when downloading | Do not modify signed URL query string |
483
+
484
+ ---
485
+
486
+ ## Examples in this repo
487
+
488
+ | File | Description |
489
+ |------|-------------|
490
+ | [`examples/basic.mjs`](./examples/basic.mjs) | Minimal JSON dump |
491
+ | [`examples/bot-example.ts`](./examples/bot-example.ts) | Generic bot handler |
492
+ | [`examples/express-api.ts`](./examples/express-api.ts) | REST API with Express |
493
+ | [`examples/fastify-api.ts`](./examples/fastify-api.ts) | REST API with Fastify |
494
+ | [`examples/telegram-bot.ts`](./examples/telegram-bot.ts) | Telegram-style handler |
495
+ | [`examples/discord-bot.ts`](./examples/discord-bot.ts) | Discord-style handler |
496
+ | [`examples/cookie-generator.mjs`](./examples/cookie-generator.mjs) | Cookie helper notes |
497
+
498
+ Run locally after build:
499
+
500
+ ```bash
501
+ npm run build
502
+ node examples/basic.mjs "https://www.instagram.com/p/SHORTCODE/"
503
+ ```
504
+
505
+ **Live Instagram tests** are kept in a separate repo folder: `ultra-igdl-live-test` (not published to npm).
506
+
507
+ ---
508
+
509
+ ## Architecture
510
+
511
+ ```
512
+ src/
513
+ ├── core/ # downloader orchestration, cache, parser, extractor
514
+ ├── extractors/ # post, reel, story, highlight
515
+ ├── network/ # undici client, headers, retry, pool, Instagram API
516
+ ├── utils/ # captions, carousel, media quality, URLs
517
+ ├── cli/ # CLI entry (published as ultra-igdl bin)
518
+ └── types/ # TypeScript definitions
519
+ ```
520
+
521
+ **Extraction layers** (first useful result wins, posts scan multiple layers for carousels):
522
+
523
+ 1. Open Graph / meta tags
524
+ 2. Embedded `application/json` / script blobs
525
+ 3. `window.__additionalDataLoaded` / `_sharedData`
526
+ 4. Next.js `__NEXT_DATA__`
527
+ 5. GraphQL / CDN discovery in HTML
528
+ 6. Regex fallback
529
+
530
+ With **session**: parallel `media/info` API for posts (carousels), reels, stories, highlights.
531
+
532
+ ---
533
+
534
+ ## Development
535
+
536
+ ```bash
537
+ git clone https://github.com/your-username/ultra-igdl.git
538
+ cd ultra-igdl
539
+ npm install
540
+ npm run build
541
+ npm test
542
+ npm run test:coverage
543
+ npm run test:stress
544
+ ```
545
+
546
+ | Script | Purpose |
547
+ |--------|---------|
548
+ | `npm run build` | Compile ESM + CJS + CLI to `dist/` |
549
+ | `npm test` | Unit + integration tests (mocked HTTP; excludes stress) |
550
+ | `npm run cli -- "<url>" --json` | Run CLI from source tree |
551
+ | `npm run lint` | Typecheck |
552
+
553
+ ---
554
+
555
+ ## Publishing to npm
556
+
557
+ Maintainers:
558
+
559
+ ```bash
560
+ # 1. Login
561
+ npm login
562
+
563
+ # 2. Set author/repository in package.json (your GitHub username)
564
+
565
+ # 3. Dry run — only dist/, README, LICENSE should be listed
566
+ npm pack --dry-run
567
+
568
+ # 4. Publish (runs build + tests via prepublishOnly)
569
+ npm publish
570
+ ```
571
+
572
+ Consumers install with:
573
+
574
+ ```bash
575
+ npm install ultra-igdl
576
+ ```
577
+
578
+ **Before first publish:** change `repository`, `bugs`, and `homepage` in `package.json` from `your-username` to your real GitHub path.
579
+
580
+ ---
581
+
582
+ ## Legal & disclaimer
583
+
584
+ - This project is **not affiliated with Instagram / Meta**.
585
+ - You are responsible for complying with Instagram's Terms of Use and applicable laws.
586
+ - Only download content you have the right to access and use.
587
+ - Session cookies are credentials — treat them like passwords.
588
+ - CDN URLs are **signed** and expire; download promptly and do not strip query parameters.
589
+
590
+ ---
591
+
592
+ ## License
593
+
594
+ [MIT](./LICENSE) © 2026 ultra-igdl contributors