imseldrith commited on
Commit
f0ffdc2
·
verified ·
1 Parent(s): 03b32ac

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +138 -33
app.py CHANGED
@@ -1,33 +1,138 @@
1
- import json
2
- import os
3
-
4
- SETTINGS_FILE = "settings.json"
5
-
6
- class Udemy:
7
- def __init__(self, mode):
8
- self.mode = mode
9
- self.settings = {}
10
-
11
- def load_settings(self):
12
- """Load user settings from JSON, handle errors if file is empty or corrupted."""
13
- try:
14
- if not os.path.exists(SETTINGS_FILE) or os.stat(SETTINGS_FILE).st_size == 0:
15
- print("⚠️ Settings file missing or empty. Creating default settings...")
16
- self.create_default_settings()
17
- with open(SETTINGS_FILE, "r") as f:
18
- self.settings = json.load(f)
19
- except (json.JSONDecodeError, ValueError):
20
- print("⚠️ Settings file is corrupted. Resetting to default settings...")
21
- self.create_default_settings()
22
-
23
- def create_default_settings(self):
24
- """Create a default settings file if it's missing or corrupted."""
25
- self.settings = {
26
- "use_browser_cookies": False,
27
- "email": "",
28
- "password": "",
29
- "scraper_sites": ["DiscUdemy", "Udemy Freebies", "Tutorial Bar"],
30
- }
31
- with open(SETTINGS_FILE, "w") as f:
32
- json.dump(self.settings, f, indent=4)
33
- print("✅ Default settings file created.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from flask import Flask, render_template_string, jsonify
2
+ from apscheduler.schedulers.background import BackgroundScheduler
3
+ import subprocess
4
+ import threading
5
+ from datetime import datetime
6
+
7
+ app = Flask(__name__)
8
+ execution_logs = []
9
+ MAX_LOG_ENTRIES = 20
10
+
11
+ def run_cli_script():
12
+ """Runs cli.py and streams logs in real-time to both UI and terminal."""
13
+ timestamp = datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S UTC")
14
+ log_entry = {'time': timestamp, 'output': '', 'error': ''}
15
+
16
+ try:
17
+ process = subprocess.Popen(
18
+ ["python", "cli.py"],
19
+ stdout=subprocess.PIPE,
20
+ stderr=subprocess.PIPE,
21
+ text=True,
22
+ bufsize=1
23
+ )
24
+
25
+ # Stream logs to UI and print to terminal
26
+ for line in process.stdout:
27
+ log_entry['output'] += line
28
+ execution_logs.append({'time': timestamp, 'output': line, 'error': ''})
29
+ print(line, end="") # Print logs to terminal
30
+ if len(execution_logs) > MAX_LOG_ENTRIES:
31
+ execution_logs.pop(0)
32
+
33
+ for line in process.stderr:
34
+ log_entry['error'] += line
35
+ execution_logs.append({'time': timestamp, 'output': '', 'error': line})
36
+ print(line, end="") # ✅ Print errors to terminal
37
+ if len(execution_logs) > MAX_LOG_ENTRIES:
38
+ execution_logs.pop(0)
39
+
40
+ except Exception as e:
41
+ log_entry['error'] = str(e)
42
+ execution_logs.append({'time': timestamp, 'output': '', 'error': str(e)})
43
+ print(f"Error: {str(e)}") # ✅ Print error to terminal
44
+
45
+ def start_initial_run():
46
+ threading.Thread(target=run_cli_script, daemon=True).start()
47
+
48
+ scheduler = BackgroundScheduler(daemon=True)
49
+ scheduler.add_job(
50
+ run_cli_script,
51
+ 'interval',
52
+ hours=3,
53
+ id='main_job',
54
+ next_run_time=datetime.now()
55
+ )
56
+ scheduler.start()
57
+
58
+ start_initial_run()
59
+
60
+ @app.route('/')
61
+ def home():
62
+ """Main UI displaying logs and next run time."""
63
+ job = scheduler.get_job('main_job')
64
+ next_run = job.next_run_time.strftime('%Y-%m-%d %H:%M:%S UTC') if job else 'N/A'
65
+
66
+ return render_template_string('''
67
+ <!DOCTYPE html>
68
+ <html>
69
+ <head>
70
+ <title>Script Scheduler</title>
71
+ <script>
72
+ function fetchLogs() {
73
+ fetch('/logs')
74
+ .then(response => response.json())
75
+ .then(data => {
76
+ let logBox = document.getElementById("log-box");
77
+ logBox.innerHTML = "";
78
+ data.logs.forEach(log => {
79
+ let logEntry = "<div class='timestamp'>" + log.time + "</div>";
80
+ if (log.output) logEntry += "<div class='output'>" + log.output + "</div>";
81
+ if (log.error) logEntry += "<div class='error'>" + log.error + "</div>";
82
+ logEntry += "<hr>";
83
+ logBox.innerHTML += logEntry;
84
+ });
85
+ logBox.scrollTop = logBox.scrollHeight;
86
+ });
87
+ }
88
+ setInterval(fetchLogs, 2000);
89
+ window.onload = fetchLogs;
90
+ </script>
91
+ <style>
92
+ body { font-family: Arial, sans-serif; padding: 20px; }
93
+ .log-box {
94
+ background: #000;
95
+ color: #0f0;
96
+ padding: 15px;
97
+ border-radius: 5px;
98
+ margin-top: 20px;
99
+ white-space: pre-wrap;
100
+ max-height: 400px;
101
+ overflow-y: auto;
102
+ }
103
+ .timestamp { color: #888; margin-bottom: 10px; }
104
+ .error { color: #ff4444; }
105
+ </style>
106
+ </head>
107
+ <body>
108
+ <h1>Script Scheduler</h1>
109
+ <p>Next run: {{ next_run }}</p>
110
+ <h2>Latest Execution Logs</h2>
111
+ <div id="log-box" class="log-box"></div>
112
+ <p><a href="/force-run">Trigger Manual Run</a></p>
113
+ <p><a href="/run-check">Check Scheduler Status</a></p>
114
+ </body>
115
+ </html>
116
+ ''', next_run=next_run)
117
+
118
+ @app.route('/logs')
119
+ def logs():
120
+ """Returns logs as JSON for AJAX polling."""
121
+ return jsonify({'logs': execution_logs})
122
+
123
+ @app.route('/force-run')
124
+ def force_run():
125
+ """Manually trigger the script execution."""
126
+ threading.Thread(target=run_cli_script, daemon=True).start()
127
+ return "Script executed manually", 200
128
+
129
+ @app.route('/run-check')
130
+ def run_check():
131
+ """Check if the scheduler is still running."""
132
+ if not scheduler.running:
133
+ print("Scheduler was stopped! Restarting...")
134
+ scheduler.start()
135
+ return "Scheduler is running", 200
136
+
137
+ if __name__ == '__main__':
138
+ app.run(host='0.0.0.0', port=7860)