vtu-auto 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.
- vtu_auto-0.1.0/PKG-INFO +36 -0
- vtu_auto-0.1.0/README.md +27 -0
- vtu_auto-0.1.0/pyproject.toml +13 -0
- vtu_auto-0.1.0/setup.cfg +4 -0
- vtu_auto-0.1.0/vtu_auto/__init__.py +1 -0
- vtu_auto-0.1.0/vtu_auto/cli.py +253 -0
- vtu_auto-0.1.0/vtu_auto.egg-info/PKG-INFO +36 -0
- vtu_auto-0.1.0/vtu_auto.egg-info/SOURCES.txt +10 -0
- vtu_auto-0.1.0/vtu_auto.egg-info/dependency_links.txt +1 -0
- vtu_auto-0.1.0/vtu_auto.egg-info/entry_points.txt +2 -0
- vtu_auto-0.1.0/vtu_auto.egg-info/requires.txt +2 -0
- vtu_auto-0.1.0/vtu_auto.egg-info/top_level.txt +1 -0
vtu_auto-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: vtu-auto
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: VTU online video progress automater
|
|
5
|
+
Requires-Python: >=3.8
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
Requires-Dist: requests>=2.33.1
|
|
8
|
+
Requires-Dist: rich>=13.0.0
|
|
9
|
+
|
|
10
|
+
# VTU Auto-Progress Bypasser
|
|
11
|
+
|
|
12
|
+
A beautiful, fast, API-driven CLI tool to automate video progress completion on the VTU portal.
|
|
13
|
+
|
|
14
|
+
## Features
|
|
15
|
+
- **API-Driven**: Completes hours of video in seconds by safely pinging the backend.
|
|
16
|
+
- **Beautiful UI**: Built with `rich` for elegant tables, spinners, and live progress bars.
|
|
17
|
+
- **Smart Pumping**: Overrides internal VTU progress bugs by dynamically pumping progress payloads until completion is acknowledged by the server.
|
|
18
|
+
- **Secure**: Takes credentials safely through prompt hiding in the terminal. No credentials are saved to disk.
|
|
19
|
+
|
|
20
|
+
## Installation
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
pip install vtu-auto
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Usage
|
|
27
|
+
|
|
28
|
+
Simply run:
|
|
29
|
+
```bash
|
|
30
|
+
vtu-auto
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
To view underlying API HTTP response trace logs locally:
|
|
34
|
+
```bash
|
|
35
|
+
vtu-auto --log
|
|
36
|
+
```
|
vtu_auto-0.1.0/README.md
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# VTU Auto-Progress Bypasser
|
|
2
|
+
|
|
3
|
+
A beautiful, fast, API-driven CLI tool to automate video progress completion on the VTU portal.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
- **API-Driven**: Completes hours of video in seconds by safely pinging the backend.
|
|
7
|
+
- **Beautiful UI**: Built with `rich` for elegant tables, spinners, and live progress bars.
|
|
8
|
+
- **Smart Pumping**: Overrides internal VTU progress bugs by dynamically pumping progress payloads until completion is acknowledged by the server.
|
|
9
|
+
- **Secure**: Takes credentials safely through prompt hiding in the terminal. No credentials are saved to disk.
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pip install vtu-auto
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Usage
|
|
18
|
+
|
|
19
|
+
Simply run:
|
|
20
|
+
```bash
|
|
21
|
+
vtu-auto
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
To view underlying API HTTP response trace logs locally:
|
|
25
|
+
```bash
|
|
26
|
+
vtu-auto --log
|
|
27
|
+
```
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "vtu-auto"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "VTU online video progress automater"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.8"
|
|
7
|
+
dependencies = [
|
|
8
|
+
"requests>=2.33.1",
|
|
9
|
+
"rich>=13.0.0",
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
[project.scripts]
|
|
13
|
+
vtu-auto = "vtu_auto.cli:main"
|
vtu_auto-0.1.0/setup.cfg
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Initialize module
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
import requests
|
|
2
|
+
import json
|
|
3
|
+
import time
|
|
4
|
+
import sys
|
|
5
|
+
import os
|
|
6
|
+
import argparse
|
|
7
|
+
import logging
|
|
8
|
+
from rich.console import Console
|
|
9
|
+
from rich.prompt import Prompt
|
|
10
|
+
from rich.panel import Panel
|
|
11
|
+
from rich.progress import Progress, SpinnerColumn, TextColumn, BarColumn, TaskProgressColumn, TimeElapsedColumn
|
|
12
|
+
from rich.syntax import Syntax
|
|
13
|
+
from rich.table import Table
|
|
14
|
+
from rich import box
|
|
15
|
+
from rich.align import Align
|
|
16
|
+
|
|
17
|
+
# Initialize Rich Console
|
|
18
|
+
console = Console()
|
|
19
|
+
|
|
20
|
+
# Setup standard logging to file
|
|
21
|
+
log_formatter = logging.Formatter('%(asctime)s | %(levelname)s | %(message)s')
|
|
22
|
+
logger = logging.getLogger('vtu_automater')
|
|
23
|
+
logger.setLevel(logging.DEBUG)
|
|
24
|
+
|
|
25
|
+
file_handler = logging.FileHandler('api_responses.log', 'w', encoding='utf-8')
|
|
26
|
+
file_handler.setFormatter(log_formatter)
|
|
27
|
+
logger.addHandler(file_handler)
|
|
28
|
+
|
|
29
|
+
BASE_URL = "https://online.vtu.ac.in/api/v1"
|
|
30
|
+
|
|
31
|
+
class VTUBypasser:
|
|
32
|
+
def __init__(self, email, password):
|
|
33
|
+
self.email = email
|
|
34
|
+
self.password = password
|
|
35
|
+
self.session = requests.Session()
|
|
36
|
+
self.session.headers.update({
|
|
37
|
+
"Accept": "application/json",
|
|
38
|
+
"Content-Type": "application/json",
|
|
39
|
+
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
|
|
40
|
+
"Origin": "https://online.vtu.ac.in",
|
|
41
|
+
"Referer": "https://online.vtu.ac.in/"
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
def login(self):
|
|
45
|
+
login_url = f"{BASE_URL}/auth/login"
|
|
46
|
+
payload = {"email": self.email, "password": self.password}
|
|
47
|
+
|
|
48
|
+
try:
|
|
49
|
+
response = self.session.post(login_url, json=payload)
|
|
50
|
+
data = response.json()
|
|
51
|
+
logger.info(f"Login Response: HTTP {response.status_code} | Body: {json.dumps(data)}")
|
|
52
|
+
|
|
53
|
+
if response.status_code == 200 and data.get("success"):
|
|
54
|
+
user_name = data.get("data", {}).get("name", "User")
|
|
55
|
+
return True, user_name
|
|
56
|
+
else:
|
|
57
|
+
msg = data.get('message', 'Unknown Error')
|
|
58
|
+
return False, msg
|
|
59
|
+
except Exception as e:
|
|
60
|
+
logger.error(f"Login Error: {e}", exc_info=True)
|
|
61
|
+
return False, str(e)
|
|
62
|
+
|
|
63
|
+
def get_enrolled_courses(self):
|
|
64
|
+
enrollments_url = f"{BASE_URL}/student/my-enrollments"
|
|
65
|
+
try:
|
|
66
|
+
response = self.session.get(enrollments_url)
|
|
67
|
+
data = response.json()
|
|
68
|
+
if response.status_code == 200 and data.get("success"):
|
|
69
|
+
return data.get("data", [])
|
|
70
|
+
except Exception as e:
|
|
71
|
+
logger.error(f"Enrollments Error: {e}", exc_info=True)
|
|
72
|
+
return []
|
|
73
|
+
|
|
74
|
+
def get_course_details(self, course_slug):
|
|
75
|
+
course_url = f"{BASE_URL}/student/my-courses/{course_slug}"
|
|
76
|
+
try:
|
|
77
|
+
response = self.session.get(course_url)
|
|
78
|
+
data = response.json()
|
|
79
|
+
if response.status_code == 200 and data.get("success"):
|
|
80
|
+
return data.get("data", {})
|
|
81
|
+
except Exception as e:
|
|
82
|
+
logger.error(f"Course Details Error for {course_slug}: {e}", exc_info=True)
|
|
83
|
+
return None
|
|
84
|
+
|
|
85
|
+
def mark_video_complete(self, course_slug, lecture_id, progress_ctx, task_id):
|
|
86
|
+
progress_url = f"{BASE_URL}/student/my-courses/{course_slug}/lectures/{lecture_id}/progress"
|
|
87
|
+
|
|
88
|
+
max_attempts = 150
|
|
89
|
+
current_time = 0
|
|
90
|
+
|
|
91
|
+
for attempt in range(1, max_attempts + 1):
|
|
92
|
+
current_time += 120
|
|
93
|
+
payload = {
|
|
94
|
+
"current_time_seconds": current_time,
|
|
95
|
+
"total_duration_seconds": 3600,
|
|
96
|
+
"seconds_just_watched": 120
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
try:
|
|
100
|
+
response = self.session.post(progress_url, json=payload)
|
|
101
|
+
res_data = response.json()
|
|
102
|
+
data_dict = res_data.get("data", {})
|
|
103
|
+
|
|
104
|
+
is_completed = data_dict.get("is_completed", False)
|
|
105
|
+
percent = data_dict.get("percent", 0)
|
|
106
|
+
|
|
107
|
+
logger.info(f"Progress Ping ({lecture_id}) Att {attempt}: HTTP {response.status_code} | Body: {json.dumps(res_data)}")
|
|
108
|
+
|
|
109
|
+
progress_ctx.update(task_id, completed=percent)
|
|
110
|
+
|
|
111
|
+
if is_completed or percent >= 98:
|
|
112
|
+
progress_ctx.update(task_id, completed=100)
|
|
113
|
+
return True, percent
|
|
114
|
+
|
|
115
|
+
if attempt == max_attempts:
|
|
116
|
+
return False, percent
|
|
117
|
+
|
|
118
|
+
time.sleep(0.5)
|
|
119
|
+
except Exception as e:
|
|
120
|
+
logger.error(f"Progress Ping Error ({lecture_id}): {e}", exc_info=True)
|
|
121
|
+
return False, -1
|
|
122
|
+
|
|
123
|
+
def view_logs():
|
|
124
|
+
if not os.path.exists('api_responses.log'):
|
|
125
|
+
console.print("[bold yellow]⚠ Log file 'api_responses.log' not found.[/bold yellow]")
|
|
126
|
+
return
|
|
127
|
+
|
|
128
|
+
console.print(Panel(Align.center("[bold cyan]◷ Recent VTU API Responses Log [/bold cyan]"), border_style="cyan", padding=(1,2)))
|
|
129
|
+
|
|
130
|
+
with open('api_responses.log', 'r', encoding='utf-8') as f:
|
|
131
|
+
lines = f.readlines()
|
|
132
|
+
recent = "".join(lines[-40:])
|
|
133
|
+
|
|
134
|
+
syntax = Syntax(recent, "log", theme="monokai", word_wrap=True, line_numbers=True)
|
|
135
|
+
console.print(syntax)
|
|
136
|
+
|
|
137
|
+
def main():
|
|
138
|
+
parser = argparse.ArgumentParser(description="VTU Online Video Automation CLI")
|
|
139
|
+
parser.add_argument("--log", action="store_true", help="View recent API response logs")
|
|
140
|
+
args = parser.parse_args()
|
|
141
|
+
|
|
142
|
+
os.system('cls' if os.name == 'nt' else 'clear')
|
|
143
|
+
|
|
144
|
+
# Title Header
|
|
145
|
+
header = Panel(
|
|
146
|
+
Align.center("[bold magenta]🎓 VTU Auto-Progress Bypasser[/bold magenta]\n[dim]v0.1.0 • API-Driven Fast Automation[/dim]"),
|
|
147
|
+
border_style="magenta",
|
|
148
|
+
box=box.DOUBLE
|
|
149
|
+
)
|
|
150
|
+
console.print(header)
|
|
151
|
+
console.print()
|
|
152
|
+
|
|
153
|
+
if args.log:
|
|
154
|
+
view_logs()
|
|
155
|
+
sys.exit(0)
|
|
156
|
+
|
|
157
|
+
# Login UI
|
|
158
|
+
with console.status("[bold blue]Ready...", spinner="point"):
|
|
159
|
+
email = Prompt.ask("[bold cyan]✉ Email[/bold cyan]")
|
|
160
|
+
password = Prompt.ask("[bold cyan]🔑 Password[/bold cyan]", password=True)
|
|
161
|
+
|
|
162
|
+
console.print()
|
|
163
|
+
|
|
164
|
+
bypasser = VTUBypasser(email, password)
|
|
165
|
+
|
|
166
|
+
with console.status("[bold yellow]Authenticating securely...", spinner="aesthetic"):
|
|
167
|
+
success, msg_or_name = bypasser.login()
|
|
168
|
+
|
|
169
|
+
if not success:
|
|
170
|
+
console.print(Panel(f"[bold red]✖ Authentication Failed:[/bold red]\n{msg_or_name}", border_style="red"))
|
|
171
|
+
sys.exit(1)
|
|
172
|
+
|
|
173
|
+
console.print(f"[bold green]✔[/bold green] Authentication successful! Welcome, [bold cyan]{msg_or_name}[/bold cyan]!")
|
|
174
|
+
|
|
175
|
+
# Fetch courses
|
|
176
|
+
with console.status("[bold yellow]Fetching your enrolled courses...", spinner="aesthetic"):
|
|
177
|
+
courses = bypasser.get_enrolled_courses()
|
|
178
|
+
|
|
179
|
+
if not courses:
|
|
180
|
+
console.print(Panel("[bold red]✖ No enrolled courses found on this account.[/bold red]", border_style="red"))
|
|
181
|
+
sys.exit(0)
|
|
182
|
+
|
|
183
|
+
# Display courses cleanly in a table
|
|
184
|
+
table = Table(title="📚 Enrolled Courses", box=box.ROUNDED, border_style="blue", title_style="bold underline")
|
|
185
|
+
table.add_column("Course ID", justify="center", style="cyan", no_wrap=True)
|
|
186
|
+
table.add_column("Course Title", style="magenta")
|
|
187
|
+
table.add_column("Progress", justify="right", style="green")
|
|
188
|
+
|
|
189
|
+
for enrollment in courses:
|
|
190
|
+
details = enrollment.get("details", {})
|
|
191
|
+
prog = enrollment.get("progress_percent", "0")
|
|
192
|
+
table.add_row(str(details.get("id", "N/A")), details.get("title", "Unknown"), f"{prog}%")
|
|
193
|
+
|
|
194
|
+
console.print("\n")
|
|
195
|
+
console.print(table)
|
|
196
|
+
console.print("\n")
|
|
197
|
+
|
|
198
|
+
for enrollment in courses:
|
|
199
|
+
details = enrollment.get("details", {})
|
|
200
|
+
course_slug = details.get("slug")
|
|
201
|
+
course_title = details.get("title")
|
|
202
|
+
|
|
203
|
+
if not course_slug:
|
|
204
|
+
continue
|
|
205
|
+
|
|
206
|
+
console.print(Panel(Align.center(f"[bold white]Processing:[/bold white] [bold yellow]{course_title}[/bold yellow]"), border_style="green"))
|
|
207
|
+
|
|
208
|
+
course_data = bypasser.get_course_details(course_slug)
|
|
209
|
+
if not course_data:
|
|
210
|
+
console.print(f"[bold red]✖ Could not load details for {course_title}[/bold red]")
|
|
211
|
+
continue
|
|
212
|
+
|
|
213
|
+
lessons = course_data.get("lessons", [])
|
|
214
|
+
|
|
215
|
+
for week in lessons:
|
|
216
|
+
week_name = week.get("name", "Unknown Week")
|
|
217
|
+
lectures = week.get("lectures", [])
|
|
218
|
+
|
|
219
|
+
console.print(f"\n[bold blue]⮞ {week_name}[/bold blue] [dim]({len(lectures)} lectures)[/dim]")
|
|
220
|
+
|
|
221
|
+
for lecture in lectures:
|
|
222
|
+
lecture_id = lecture.get("id")
|
|
223
|
+
lecture_title = lecture.get("title", f"Lecture {lecture_id}")
|
|
224
|
+
|
|
225
|
+
# We use a beautiful progress bar for every single video
|
|
226
|
+
with Progress(
|
|
227
|
+
SpinnerColumn(),
|
|
228
|
+
TextColumn("[bold cyan]{task.description}"),
|
|
229
|
+
BarColumn(complete_style="green", finished_style="bold green"),
|
|
230
|
+
TaskProgressColumn(),
|
|
231
|
+
TimeElapsedColumn(),
|
|
232
|
+
console=console,
|
|
233
|
+
transient=False # Keep it printed afterwards
|
|
234
|
+
) as progress:
|
|
235
|
+
|
|
236
|
+
# Target is 100%
|
|
237
|
+
task_id = progress.add_task(f"{lecture_title}", total=100, completed=0)
|
|
238
|
+
|
|
239
|
+
success, final_percent = bypasser.mark_video_complete(course_slug, lecture_id, progress, task_id)
|
|
240
|
+
|
|
241
|
+
if not success:
|
|
242
|
+
if final_percent == -1:
|
|
243
|
+
progress.update(task_id, description=f"[bold red]✖ {lecture_title} (Network Error)[/bold red]")
|
|
244
|
+
else:
|
|
245
|
+
progress.update(task_id, description=f"[bold yellow]⚠ {lecture_title} (Capped at {final_percent}%)[/bold yellow]")
|
|
246
|
+
else:
|
|
247
|
+
progress.update(task_id, description=f"[bold green]✔ {lecture_title} (Matched 100%)[/bold green]")
|
|
248
|
+
|
|
249
|
+
console.print("\n")
|
|
250
|
+
console.print(Panel(Align.center("[bold green]🎉 All tasks fully finished! You can check the portal now![/bold green]"), border_style="green"))
|
|
251
|
+
|
|
252
|
+
if __name__ == "__main__":
|
|
253
|
+
main()
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: vtu-auto
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: VTU online video progress automater
|
|
5
|
+
Requires-Python: >=3.8
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
Requires-Dist: requests>=2.33.1
|
|
8
|
+
Requires-Dist: rich>=13.0.0
|
|
9
|
+
|
|
10
|
+
# VTU Auto-Progress Bypasser
|
|
11
|
+
|
|
12
|
+
A beautiful, fast, API-driven CLI tool to automate video progress completion on the VTU portal.
|
|
13
|
+
|
|
14
|
+
## Features
|
|
15
|
+
- **API-Driven**: Completes hours of video in seconds by safely pinging the backend.
|
|
16
|
+
- **Beautiful UI**: Built with `rich` for elegant tables, spinners, and live progress bars.
|
|
17
|
+
- **Smart Pumping**: Overrides internal VTU progress bugs by dynamically pumping progress payloads until completion is acknowledged by the server.
|
|
18
|
+
- **Secure**: Takes credentials safely through prompt hiding in the terminal. No credentials are saved to disk.
|
|
19
|
+
|
|
20
|
+
## Installation
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
pip install vtu-auto
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Usage
|
|
27
|
+
|
|
28
|
+
Simply run:
|
|
29
|
+
```bash
|
|
30
|
+
vtu-auto
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
To view underlying API HTTP response trace logs locally:
|
|
34
|
+
```bash
|
|
35
|
+
vtu-auto --log
|
|
36
|
+
```
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
vtu_auto/__init__.py
|
|
4
|
+
vtu_auto/cli.py
|
|
5
|
+
vtu_auto.egg-info/PKG-INFO
|
|
6
|
+
vtu_auto.egg-info/SOURCES.txt
|
|
7
|
+
vtu_auto.egg-info/dependency_links.txt
|
|
8
|
+
vtu_auto.egg-info/entry_points.txt
|
|
9
|
+
vtu_auto.egg-info/requires.txt
|
|
10
|
+
vtu_auto.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
vtu_auto
|