insta-cli-sudeep 0.1.0__tar.gz
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.
- insta_cli_sudeep-0.1.0/PKG-INFO +65 -0
- insta_cli_sudeep-0.1.0/README.md +51 -0
- insta_cli_sudeep-0.1.0/insta_cli/__init__.py +1 -0
- insta_cli_sudeep-0.1.0/insta_cli/auth.py +59 -0
- insta_cli_sudeep-0.1.0/insta_cli/browse.py +76 -0
- insta_cli_sudeep-0.1.0/insta_cli/cli.py +88 -0
- insta_cli_sudeep-0.1.0/insta_cli/download.py +95 -0
- insta_cli_sudeep-0.1.0/insta_cli_sudeep.egg-info/PKG-INFO +65 -0
- insta_cli_sudeep-0.1.0/insta_cli_sudeep.egg-info/SOURCES.txt +13 -0
- insta_cli_sudeep-0.1.0/insta_cli_sudeep.egg-info/dependency_links.txt +1 -0
- insta_cli_sudeep-0.1.0/insta_cli_sudeep.egg-info/entry_points.txt +2 -0
- insta_cli_sudeep-0.1.0/insta_cli_sudeep.egg-info/requires.txt +3 -0
- insta_cli_sudeep-0.1.0/insta_cli_sudeep.egg-info/top_level.txt +1 -0
- insta_cli_sudeep-0.1.0/pyproject.toml +26 -0
- insta_cli_sudeep-0.1.0/setup.cfg +4 -0
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: insta-cli-sudeep
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A terminal-based Instagram browser and downloader.
|
|
5
|
+
Author-email: Sudeep <your.email@example.com>
|
|
6
|
+
Classifier: Programming Language :: Python :: 3
|
|
7
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
8
|
+
Classifier: Operating System :: OS Independent
|
|
9
|
+
Requires-Python: >=3.8
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
Requires-Dist: instagrapi>=2.0.0
|
|
12
|
+
Requires-Dist: rich>=13.0.0
|
|
13
|
+
Requires-Dist: yt-dlp>=2024.1.0
|
|
14
|
+
|
|
15
|
+
# insta-cli-sudeep 📸
|
|
16
|
+
|
|
17
|
+
A terminal-based Instagram browser and downloader. Browse profiles, download posts, reels, and stories — all from your command line.
|
|
18
|
+
|
|
19
|
+
## Installation
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
pip install insta-cli-sudeep
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Usage
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
insta
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
You'll be prompted to log in with your Instagram credentials. Your session is saved locally so you only log in once.
|
|
32
|
+
|
|
33
|
+
## Features
|
|
34
|
+
|
|
35
|
+
- 🔐 **Login with session saving** — log in once, reuse session forever
|
|
36
|
+
- 👤 **Browse profiles** — view posts, reels, likes, captions in a clean table
|
|
37
|
+
- 🎬 **Download posts & reels** — by URL or directly from profile browser
|
|
38
|
+
- 📖 **Download stories** — grab all active stories from any user
|
|
39
|
+
- 🔄 **yt-dlp fallback** — automatic fallback for tricky downloads
|
|
40
|
+
|
|
41
|
+
## Downloaded files
|
|
42
|
+
|
|
43
|
+
All downloads are saved to `~/insta_downloads/`:
|
|
44
|
+
```
|
|
45
|
+
~/insta_downloads/
|
|
46
|
+
├── posts/
|
|
47
|
+
├── reels/
|
|
48
|
+
└── stories/
|
|
49
|
+
└── <username>/
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## ⚠️ Important
|
|
53
|
+
|
|
54
|
+
- Use a **secondary Instagram account** for testing — not your main one
|
|
55
|
+
- This tool uses `instagrapi` which mimics the Instagram mobile app
|
|
56
|
+
- Respect Instagram's Terms of Service and only download content you have rights to
|
|
57
|
+
|
|
58
|
+
## Requirements
|
|
59
|
+
|
|
60
|
+
- Python 3.8+
|
|
61
|
+
- `instagrapi`, `rich`, `yt-dlp` (installed automatically)
|
|
62
|
+
|
|
63
|
+
## License
|
|
64
|
+
|
|
65
|
+
MIT
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# insta-cli-sudeep 📸
|
|
2
|
+
|
|
3
|
+
A terminal-based Instagram browser and downloader. Browse profiles, download posts, reels, and stories — all from your command line.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install insta-cli-sudeep
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
insta
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
You'll be prompted to log in with your Instagram credentials. Your session is saved locally so you only log in once.
|
|
18
|
+
|
|
19
|
+
## Features
|
|
20
|
+
|
|
21
|
+
- 🔐 **Login with session saving** — log in once, reuse session forever
|
|
22
|
+
- 👤 **Browse profiles** — view posts, reels, likes, captions in a clean table
|
|
23
|
+
- 🎬 **Download posts & reels** — by URL or directly from profile browser
|
|
24
|
+
- 📖 **Download stories** — grab all active stories from any user
|
|
25
|
+
- 🔄 **yt-dlp fallback** — automatic fallback for tricky downloads
|
|
26
|
+
|
|
27
|
+
## Downloaded files
|
|
28
|
+
|
|
29
|
+
All downloads are saved to `~/insta_downloads/`:
|
|
30
|
+
```
|
|
31
|
+
~/insta_downloads/
|
|
32
|
+
├── posts/
|
|
33
|
+
├── reels/
|
|
34
|
+
└── stories/
|
|
35
|
+
└── <username>/
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## ⚠️ Important
|
|
39
|
+
|
|
40
|
+
- Use a **secondary Instagram account** for testing — not your main one
|
|
41
|
+
- This tool uses `instagrapi` which mimics the Instagram mobile app
|
|
42
|
+
- Respect Instagram's Terms of Service and only download content you have rights to
|
|
43
|
+
|
|
44
|
+
## Requirements
|
|
45
|
+
|
|
46
|
+
- Python 3.8+
|
|
47
|
+
- `instagrapi`, `rich`, `yt-dlp` (installed automatically)
|
|
48
|
+
|
|
49
|
+
## License
|
|
50
|
+
|
|
51
|
+
MIT
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# insta-cli by sudeep
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import json
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from instagrapi import Client
|
|
5
|
+
from instagrapi.exceptions import LoginRequired, TwoFactorRequired, BadPassword
|
|
6
|
+
|
|
7
|
+
SESSION_FILE = Path.home() / ".insta_cli_session.json"
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def get_client() -> Client:
|
|
11
|
+
cl = Client()
|
|
12
|
+
cl.delay_range = [1, 3] # Be polite, avoid rate limits
|
|
13
|
+
return cl
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def login(username: str = None, password: str = None) -> Client:
|
|
17
|
+
cl = get_client()
|
|
18
|
+
|
|
19
|
+
# Try loading existing session first
|
|
20
|
+
if SESSION_FILE.exists():
|
|
21
|
+
try:
|
|
22
|
+
session = json.loads(SESSION_FILE.read_text())
|
|
23
|
+
cl.set_settings(session)
|
|
24
|
+
cl.login(session["username"], "") # reuse session
|
|
25
|
+
cl.get_timeline_feed() # test if session is valid
|
|
26
|
+
return cl
|
|
27
|
+
except Exception:
|
|
28
|
+
SESSION_FILE.unlink(missing_ok=True)
|
|
29
|
+
|
|
30
|
+
# Fresh login
|
|
31
|
+
if not username:
|
|
32
|
+
username = input("Instagram username: ").strip()
|
|
33
|
+
if not password:
|
|
34
|
+
import getpass
|
|
35
|
+
password = getpass.getpass("Instagram password: ")
|
|
36
|
+
|
|
37
|
+
try:
|
|
38
|
+
cl.login(username, password)
|
|
39
|
+
except TwoFactorRequired:
|
|
40
|
+
code = input("2FA code: ").strip()
|
|
41
|
+
cl.login(username, password, verification_code=code)
|
|
42
|
+
except BadPassword:
|
|
43
|
+
print("❌ Wrong password. Try again.")
|
|
44
|
+
raise SystemExit(1)
|
|
45
|
+
|
|
46
|
+
# Save session
|
|
47
|
+
settings = cl.get_settings()
|
|
48
|
+
settings["username"] = username
|
|
49
|
+
SESSION_FILE.write_text(json.dumps(settings))
|
|
50
|
+
print(f"✅ Logged in as @{username} (session saved)")
|
|
51
|
+
return cl
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def logout():
|
|
55
|
+
if SESSION_FILE.exists():
|
|
56
|
+
SESSION_FILE.unlink()
|
|
57
|
+
print("✅ Logged out and session cleared.")
|
|
58
|
+
else:
|
|
59
|
+
print("No active session found.")
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
from instagrapi import Client
|
|
2
|
+
from rich.console import Console
|
|
3
|
+
from rich.table import Table
|
|
4
|
+
from rich import box
|
|
5
|
+
from rich.panel import Panel
|
|
6
|
+
from rich.text import Text
|
|
7
|
+
|
|
8
|
+
console = Console()
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def browse_profile(cl: Client):
|
|
12
|
+
username = input("\nEnter username to browse: ").strip().lstrip("@")
|
|
13
|
+
|
|
14
|
+
try:
|
|
15
|
+
user = cl.user_info_by_username(username)
|
|
16
|
+
except Exception as e:
|
|
17
|
+
console.print(f"[red]Could not find user @{username}: {e}[/red]")
|
|
18
|
+
return
|
|
19
|
+
|
|
20
|
+
# Display profile info
|
|
21
|
+
console.print()
|
|
22
|
+
console.print(Panel(
|
|
23
|
+
f"[bold cyan]@{user.username}[/bold cyan]\n"
|
|
24
|
+
f"[white]{user.full_name}[/white]\n\n"
|
|
25
|
+
f"[yellow]{user.biography or 'No bio'}[/yellow]\n\n"
|
|
26
|
+
f"[green]Posts:[/green] {user.media_count} "
|
|
27
|
+
f"[green]Followers:[/green] {user.follower_count:,} "
|
|
28
|
+
f"[green]Following:[/green] {user.following_count:,}\n"
|
|
29
|
+
f"[dim]{'🔒 Private' if user.is_private else '🌐 Public'} "
|
|
30
|
+
f"{'✅ Verified' if user.is_verified else ''}[/dim]",
|
|
31
|
+
title="Profile",
|
|
32
|
+
border_style="cyan"
|
|
33
|
+
))
|
|
34
|
+
|
|
35
|
+
if user.is_private:
|
|
36
|
+
console.print("[yellow]⚠ This account is private.[/yellow]")
|
|
37
|
+
return
|
|
38
|
+
|
|
39
|
+
# Show recent posts
|
|
40
|
+
console.print("\n[bold]Recent Posts:[/bold]")
|
|
41
|
+
try:
|
|
42
|
+
medias = cl.user_medias(user.pk, amount=12)
|
|
43
|
+
except Exception as e:
|
|
44
|
+
console.print(f"[red]Could not fetch posts: {e}[/red]")
|
|
45
|
+
return
|
|
46
|
+
|
|
47
|
+
table = Table(box=box.ROUNDED, show_header=True, header_style="bold magenta")
|
|
48
|
+
table.add_column("#", style="dim", width=4)
|
|
49
|
+
table.add_column("Type", width=8)
|
|
50
|
+
table.add_column("Likes", justify="right", width=8)
|
|
51
|
+
table.add_column("Comments", justify="right", width=10)
|
|
52
|
+
table.add_column("Caption", width=50)
|
|
53
|
+
table.add_column("URL", width=40)
|
|
54
|
+
|
|
55
|
+
for i, media in enumerate(medias, 1):
|
|
56
|
+
media_type = {1: "📷 Photo", 2: "🎬 Reel", 8: "🖼 Album"}.get(media.media_type, "?")
|
|
57
|
+
caption = (media.caption_text or "")[:60].replace("\n", " ")
|
|
58
|
+
url = f"https://www.instagram.com/p/{media.code}/"
|
|
59
|
+
table.add_row(
|
|
60
|
+
str(i),
|
|
61
|
+
media_type,
|
|
62
|
+
str(media.like_count),
|
|
63
|
+
str(media.comment_count),
|
|
64
|
+
caption,
|
|
65
|
+
url
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
console.print(table)
|
|
69
|
+
|
|
70
|
+
# Offer to download
|
|
71
|
+
choice = input("\nDownload a post? Enter number (or press Enter to skip): ").strip()
|
|
72
|
+
if choice.isdigit():
|
|
73
|
+
idx = int(choice) - 1
|
|
74
|
+
if 0 <= idx < len(medias):
|
|
75
|
+
return medias[idx]
|
|
76
|
+
return None
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
from rich.console import Console
|
|
3
|
+
from rich.panel import Panel
|
|
4
|
+
from rich.text import Text
|
|
5
|
+
from rich.align import Align
|
|
6
|
+
|
|
7
|
+
console = Console()
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def print_banner():
|
|
11
|
+
banner = Text()
|
|
12
|
+
banner.append(" ██╗███╗ ██╗███████╗████████╗ █████╗ \n", style="bold magenta")
|
|
13
|
+
banner.append(" ██║████╗ ██║██╔════╝╚══██╔══╝██╔══██╗\n", style="bold magenta")
|
|
14
|
+
banner.append(" ██║██╔██╗ ██║███████╗ ██║ ███████║\n", style="bold cyan")
|
|
15
|
+
banner.append(" ██║██║╚██╗██║╚════██║ ██║ ██╔══██║\n", style="bold cyan")
|
|
16
|
+
banner.append(" ██║██║ ╚████║███████║ ██║ ██║ ██║\n", style="bold blue")
|
|
17
|
+
banner.append(" ╚═╝╚═╝ ╚═══╝╚══════╝ ╚═╝ ╚═╝ ╚═╝\n", style="bold blue")
|
|
18
|
+
banner.append(" CLI 📸 by sudeep", style="dim white")
|
|
19
|
+
|
|
20
|
+
console.print(Panel(Align.center(banner), border_style="magenta", padding=(1, 4)))
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def print_menu():
|
|
24
|
+
console.print("\n[bold white]What do you want to do?[/bold white]\n")
|
|
25
|
+
console.print(" [bold cyan][1][/bold cyan] Browse a profile")
|
|
26
|
+
console.print(" [bold cyan][2][/bold cyan] Download post / reel by URL")
|
|
27
|
+
console.print(" [bold cyan][3][/bold cyan] Download stories")
|
|
28
|
+
console.print(" [bold cyan][4][/bold cyan] Logout")
|
|
29
|
+
console.print(" [bold cyan][0][/bold cyan] Exit\n")
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def main():
|
|
33
|
+
from insta_cli.auth import login, logout
|
|
34
|
+
from insta_cli.browse import browse_profile
|
|
35
|
+
from insta_cli.download import download_media, download_stories, download_by_url
|
|
36
|
+
|
|
37
|
+
console.clear()
|
|
38
|
+
print_banner()
|
|
39
|
+
console.print("\n[dim]Logging in...[/dim]")
|
|
40
|
+
|
|
41
|
+
try:
|
|
42
|
+
cl = login()
|
|
43
|
+
except KeyboardInterrupt:
|
|
44
|
+
console.print("\n[yellow]Cancelled.[/yellow]")
|
|
45
|
+
sys.exit(0)
|
|
46
|
+
except SystemExit:
|
|
47
|
+
sys.exit(1)
|
|
48
|
+
|
|
49
|
+
while True:
|
|
50
|
+
print_menu()
|
|
51
|
+
|
|
52
|
+
try:
|
|
53
|
+
choice = input(" Enter choice: ").strip()
|
|
54
|
+
except KeyboardInterrupt:
|
|
55
|
+
console.print("\n[yellow]Bye! 👋[/yellow]")
|
|
56
|
+
break
|
|
57
|
+
|
|
58
|
+
if choice == "1":
|
|
59
|
+
selected_media = browse_profile(cl)
|
|
60
|
+
if selected_media:
|
|
61
|
+
download_media(cl, selected_media)
|
|
62
|
+
|
|
63
|
+
elif choice == "2":
|
|
64
|
+
url = input("\nPaste Instagram URL: ").strip()
|
|
65
|
+
download_by_url(url)
|
|
66
|
+
|
|
67
|
+
elif choice == "3":
|
|
68
|
+
download_stories(cl)
|
|
69
|
+
|
|
70
|
+
elif choice == "4":
|
|
71
|
+
logout()
|
|
72
|
+
console.print("[yellow]Session cleared. Restart to log in again.[/yellow]")
|
|
73
|
+
break
|
|
74
|
+
|
|
75
|
+
elif choice == "0":
|
|
76
|
+
console.print("\n[yellow]Bye! 👋[/yellow]")
|
|
77
|
+
break
|
|
78
|
+
|
|
79
|
+
else:
|
|
80
|
+
console.print("[red]Invalid choice. Try again.[/red]")
|
|
81
|
+
|
|
82
|
+
input("\n[Press Enter to continue]")
|
|
83
|
+
console.clear()
|
|
84
|
+
print_banner()
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
if __name__ == "__main__":
|
|
88
|
+
main()
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import subprocess
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from instagrapi import Client
|
|
5
|
+
from rich.console import Console
|
|
6
|
+
from rich.progress import Progress, SpinnerColumn, TextColumn
|
|
7
|
+
|
|
8
|
+
console = Console()
|
|
9
|
+
DOWNLOAD_DIR = Path.home() / "insta_downloads"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def ensure_dir(path: Path):
|
|
13
|
+
path.mkdir(parents=True, exist_ok=True)
|
|
14
|
+
return path
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def download_by_url(url: str):
|
|
18
|
+
"""Use yt-dlp to download a reel or post by URL."""
|
|
19
|
+
out_dir = ensure_dir(DOWNLOAD_DIR / "reels")
|
|
20
|
+
console.print(f"\n[cyan]Downloading via yt-dlp...[/cyan]")
|
|
21
|
+
result = subprocess.run(
|
|
22
|
+
["yt-dlp", "-o", str(out_dir / "%(title)s.%(ext)s"), url],
|
|
23
|
+
capture_output=False
|
|
24
|
+
)
|
|
25
|
+
if result.returncode == 0:
|
|
26
|
+
console.print(f"[green]✅ Saved to {out_dir}[/green]")
|
|
27
|
+
else:
|
|
28
|
+
console.print("[red]❌ yt-dlp failed. Make sure it's installed: pip install yt-dlp[/red]")
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def download_media(cl: Client, media=None):
|
|
32
|
+
"""Download a post/reel using instagrapi or yt-dlp."""
|
|
33
|
+
if media is None:
|
|
34
|
+
url = input("\nPaste Instagram post/reel URL: ").strip()
|
|
35
|
+
download_by_url(url)
|
|
36
|
+
return
|
|
37
|
+
|
|
38
|
+
out_dir = ensure_dir(DOWNLOAD_DIR / "posts")
|
|
39
|
+
|
|
40
|
+
with Progress(SpinnerColumn(), TextColumn("[progress.description]{task.description}")) as progress:
|
|
41
|
+
task = progress.add_task("Downloading...", total=None)
|
|
42
|
+
|
|
43
|
+
try:
|
|
44
|
+
if media.media_type == 1: # Photo
|
|
45
|
+
path = cl.photo_download(media.pk, folder=out_dir)
|
|
46
|
+
progress.update(task, description="Downloaded photo ✅")
|
|
47
|
+
elif media.media_type == 2: # Video/Reel
|
|
48
|
+
path = cl.video_download(media.pk, folder=out_dir)
|
|
49
|
+
progress.update(task, description="Downloaded reel ✅")
|
|
50
|
+
elif media.media_type == 8: # Album
|
|
51
|
+
paths = cl.album_download(media.pk, folder=out_dir)
|
|
52
|
+
progress.update(task, description=f"Downloaded {len(paths)} items ✅")
|
|
53
|
+
console.print(f"[green]✅ Album saved to {out_dir}[/green]")
|
|
54
|
+
return
|
|
55
|
+
else:
|
|
56
|
+
progress.update(task, description="Unknown media type")
|
|
57
|
+
return
|
|
58
|
+
|
|
59
|
+
console.print(f"[green]✅ Saved to {path}[/green]")
|
|
60
|
+
except Exception as e:
|
|
61
|
+
console.print(f"[red]❌ Download failed: {e}[/red]")
|
|
62
|
+
console.print("[yellow]Trying yt-dlp fallback...[/yellow]")
|
|
63
|
+
url = f"https://www.instagram.com/p/{media.code}/"
|
|
64
|
+
download_by_url(url)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def download_stories(cl: Client):
|
|
68
|
+
"""Download all stories from a user."""
|
|
69
|
+
username = input("\nEnter username to grab stories from: ").strip().lstrip("@")
|
|
70
|
+
|
|
71
|
+
try:
|
|
72
|
+
user = cl.user_info_by_username(username)
|
|
73
|
+
stories = cl.user_stories(user.pk)
|
|
74
|
+
except Exception as e:
|
|
75
|
+
console.print(f"[red]❌ Could not fetch stories: {e}[/red]")
|
|
76
|
+
return
|
|
77
|
+
|
|
78
|
+
if not stories:
|
|
79
|
+
console.print(f"[yellow]No active stories for @{username}[/yellow]")
|
|
80
|
+
return
|
|
81
|
+
|
|
82
|
+
out_dir = ensure_dir(DOWNLOAD_DIR / "stories" / username)
|
|
83
|
+
console.print(f"\n[cyan]Found {len(stories)} stories. Downloading...[/cyan]")
|
|
84
|
+
|
|
85
|
+
for i, story in enumerate(stories, 1):
|
|
86
|
+
try:
|
|
87
|
+
if story.media_type == 1:
|
|
88
|
+
path = cl.photo_download(story.pk, folder=out_dir)
|
|
89
|
+
else:
|
|
90
|
+
path = cl.video_download(story.pk, folder=out_dir)
|
|
91
|
+
console.print(f" [{i}/{len(stories)}] ✅ {Path(path).name}")
|
|
92
|
+
except Exception as e:
|
|
93
|
+
console.print(f" [{i}/{len(stories)}] ❌ Failed: {e}")
|
|
94
|
+
|
|
95
|
+
console.print(f"\n[green]✅ Stories saved to {out_dir}[/green]")
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: insta-cli-sudeep
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A terminal-based Instagram browser and downloader.
|
|
5
|
+
Author-email: Sudeep <your.email@example.com>
|
|
6
|
+
Classifier: Programming Language :: Python :: 3
|
|
7
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
8
|
+
Classifier: Operating System :: OS Independent
|
|
9
|
+
Requires-Python: >=3.8
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
Requires-Dist: instagrapi>=2.0.0
|
|
12
|
+
Requires-Dist: rich>=13.0.0
|
|
13
|
+
Requires-Dist: yt-dlp>=2024.1.0
|
|
14
|
+
|
|
15
|
+
# insta-cli-sudeep 📸
|
|
16
|
+
|
|
17
|
+
A terminal-based Instagram browser and downloader. Browse profiles, download posts, reels, and stories — all from your command line.
|
|
18
|
+
|
|
19
|
+
## Installation
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
pip install insta-cli-sudeep
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Usage
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
insta
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
You'll be prompted to log in with your Instagram credentials. Your session is saved locally so you only log in once.
|
|
32
|
+
|
|
33
|
+
## Features
|
|
34
|
+
|
|
35
|
+
- 🔐 **Login with session saving** — log in once, reuse session forever
|
|
36
|
+
- 👤 **Browse profiles** — view posts, reels, likes, captions in a clean table
|
|
37
|
+
- 🎬 **Download posts & reels** — by URL or directly from profile browser
|
|
38
|
+
- 📖 **Download stories** — grab all active stories from any user
|
|
39
|
+
- 🔄 **yt-dlp fallback** — automatic fallback for tricky downloads
|
|
40
|
+
|
|
41
|
+
## Downloaded files
|
|
42
|
+
|
|
43
|
+
All downloads are saved to `~/insta_downloads/`:
|
|
44
|
+
```
|
|
45
|
+
~/insta_downloads/
|
|
46
|
+
├── posts/
|
|
47
|
+
├── reels/
|
|
48
|
+
└── stories/
|
|
49
|
+
└── <username>/
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## ⚠️ Important
|
|
53
|
+
|
|
54
|
+
- Use a **secondary Instagram account** for testing — not your main one
|
|
55
|
+
- This tool uses `instagrapi` which mimics the Instagram mobile app
|
|
56
|
+
- Respect Instagram's Terms of Service and only download content you have rights to
|
|
57
|
+
|
|
58
|
+
## Requirements
|
|
59
|
+
|
|
60
|
+
- Python 3.8+
|
|
61
|
+
- `instagrapi`, `rich`, `yt-dlp` (installed automatically)
|
|
62
|
+
|
|
63
|
+
## License
|
|
64
|
+
|
|
65
|
+
MIT
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
insta_cli/__init__.py
|
|
4
|
+
insta_cli/auth.py
|
|
5
|
+
insta_cli/browse.py
|
|
6
|
+
insta_cli/cli.py
|
|
7
|
+
insta_cli/download.py
|
|
8
|
+
insta_cli_sudeep.egg-info/PKG-INFO
|
|
9
|
+
insta_cli_sudeep.egg-info/SOURCES.txt
|
|
10
|
+
insta_cli_sudeep.egg-info/dependency_links.txt
|
|
11
|
+
insta_cli_sudeep.egg-info/entry_points.txt
|
|
12
|
+
insta_cli_sudeep.egg-info/requires.txt
|
|
13
|
+
insta_cli_sudeep.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
insta_cli
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "insta-cli-sudeep"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
authors = [
|
|
9
|
+
{ name="Sudeep", email="your.email@example.com" },
|
|
10
|
+
]
|
|
11
|
+
description = "A terminal-based Instagram browser and downloader."
|
|
12
|
+
readme = "README.md"
|
|
13
|
+
requires-python = ">=3.8"
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Programming Language :: Python :: 3",
|
|
16
|
+
"License :: OSI Approved :: MIT License",
|
|
17
|
+
"Operating System :: OS Independent",
|
|
18
|
+
]
|
|
19
|
+
dependencies = [
|
|
20
|
+
"instagrapi>=2.0.0",
|
|
21
|
+
"rich>=13.0.0",
|
|
22
|
+
"yt-dlp>=2024.1.0",
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
[project.scripts]
|
|
26
|
+
insta = "insta_cli.cli:main"
|