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.
@@ -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,2 @@
1
+ [console_scripts]
2
+ insta = insta_cli.cli:main
@@ -0,0 +1,3 @@
1
+ instagrapi>=2.0.0
2
+ rich>=13.0.0
3
+ yt-dlp>=2024.1.0
@@ -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"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+