NitinBot002 commited on
Commit
bc131a3
·
verified ·
1 Parent(s): 074fe33

Rename templates/auth.html to templates/dashboard.html

Browse files
Files changed (2) hide show
  1. templates/auth.html +0 -23
  2. templates/dashboard.html +337 -0
templates/auth.html DELETED
@@ -1,23 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>Authenticate YouTube</title>
7
- <style>
8
- body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; display: flex; align-items: center; justify-content: center; height: 100vh; background-color: #f4f7f6; margin: 0; }
9
- .auth-container { text-align: center; background-color: white; padding: 40px; border-radius: 8px; box-shadow: 0 4px 8px rgba(0,0,0,0.1); }
10
- h1 { color: #333; margin-bottom: 20px; }
11
- p { color: #666; margin-bottom: 30px; }
12
- .btn-auth { display: inline-block; padding: 15px 30px; background-color: #c72c1c; color: white; border: none; border-radius: 5px; cursor: pointer; font-size: 16px; text-decoration: none; font-weight: bold; }
13
- .btn-auth:hover { background-color: #a62415; }
14
- </style>
15
- </head>
16
- <body>
17
- <div class="auth-container">
18
- <h1>YouTube Authentication</h1>
19
- <p>Grant permission to upload videos to your YouTube account.</p>
20
- <a href="{{ url_for('authorize') }}" class="btn-auth">Authenticate with YouTube</a>
21
- </div>
22
- </body>
23
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
templates/dashboard.html ADDED
@@ -0,0 +1,337 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {% extends "base.html" %}
2
+
3
+ {% block title %}Dashboard - Telegram to YouTube Uploader{% endblock %}
4
+
5
+ {% block content %}
6
+ <h2>Dashboard</h2>
7
+
8
+ <!-- Configuration Section -->
9
+ <div class="card mb-4">
10
+ <div class="card-header">
11
+ <h5>Configuration</h5>
12
+ </div>
13
+ <div class="card-body">
14
+ <form id="configForm">
15
+ <div class="row">
16
+ <div class="col-md-6 mb-3">
17
+ <label for="telegram_channel_username" class="form-label">Telegram Channel Username *</label>
18
+ <input type="text" class="form-control" id="telegram_channel_username" name="telegram_channel_username" value="{{ config.TELEGRAM_CHANNEL_USERNAME or '' }}" placeholder="@your_channel">
19
+ <div class="form-text">The username of the Telegram channel to monitor (e.g., @mychannel).</div>
20
+ </div>
21
+ <div class="col-md-6 mb-3">
22
+ <label for="youtube_title_prefix" class="form-label">YouTube Title Prefix</label>
23
+ <input type="text" class="form-control" id="youtube_title_prefix" name="youtube_title_prefix" value="{{ config.YOUTUBE_TITLE_PREFIX or '[TG2YT] ' }}">
24
+ <div class="form-text">Text to prepend to the Telegram video caption for the YouTube title.</div>
25
+ </div>
26
+ </div>
27
+ <div class="mb-3">
28
+ <label for="youtube_description_template" class="form-label">YouTube Description Template</label>
29
+ <textarea class="form-control" id="youtube_description_template" name="youtube_description_template" rows="2">{{ config.YOUTUBE_DESCRIPTION_TEMPLATE or 'Video automatically uploaded from Telegram.' }}</textarea>
30
+ <div class="form-text">Base description for uploaded videos. Original Telegram post link and caption will be appended.</div>
31
+ </div>
32
+ <div class="row">
33
+ <div class="col-md-6 mb-3">
34
+ <label for="youtube_tags" class="form-label">YouTube Tags</label>
35
+ <input type="text" class="form-control" id="youtube_tags" name="youtube_tags" value="{{ config.YOUTUBE_TAGS or 'telegram,upload,automation' }}">
36
+ <div class="form-text">Comma-separated list of tags for YouTube videos.</div>
37
+ </div>
38
+ <div class="col-md-3 mb-3">
39
+ <label for="youtube_category_id" class="form-label">YouTube Category ID</label>
40
+ <input type="text" class="form-control" id="youtube_category_id" name="youtube_category_id" value="{{ config.YOUTUBE_CATEGORY_ID or '22' }}">
41
+ <div class="form-text">Numeric ID for the YouTube video category (22 = People & Blogs).</div>
42
+ </div>
43
+ <div class="col-md-3 mb-3">
44
+ <label for="youtube_privacy_status" class="form-label">YouTube Privacy Status</label>
45
+ <select class="form-select" id="youtube_privacy_status" name="youtube_privacy_status">
46
+ <option value="private" {% if (config.YOUTUBE_PRIVACY_STATUS or 'private') == 'private' %}selected{% endif %}>Private</option>
47
+ <option value="public" {% if (config.YOUTUBE_PRIVACY_STATUS or 'private') == 'public' %}selected{% endif %}>Public</option>
48
+ <option value="unlisted" {% if (config.YOUTUBE_PRIVACY_STATUS or 'private') == 'unlisted' %}selected{% endif %}>Unlisted</option>
49
+ </select>
50
+ <div class="form-text">Initial privacy setting for uploaded videos.</div>
51
+ </div>
52
+ </div>
53
+ <button type="submit" class="btn btn-primary" id="saveConfigBtn">Save Configuration</button>
54
+ <div id="configSaveStatus" class="mt-2"></div>
55
+ </form>
56
+ </div>
57
+ </div>
58
+
59
+ <!-- Existing Status and Control Sections -->
60
+ <div class="row">
61
+ <div class="col-md-6">
62
+ <div class="card status-card">
63
+ <div class="card-header">
64
+ <h5>Authentication Status</h5>
65
+ </div>
66
+ <div class="card-body">
67
+ <p><strong>YouTube:</strong>
68
+ {% if status.youtube_authenticated %}
69
+ <span class="badge bg-success">Authenticated</span>
70
+ <a href="{{ url_for('youtube_auth') }}" class="btn btn-sm btn-outline-primary">Re-authenticate</a>
71
+ {% else %}
72
+ <span class="badge bg-warning text-dark">Not Authenticated</span>
73
+ <a href="{{ url_for('youtube_auth') }}" class="btn btn-sm btn-primary">Authenticate</a>
74
+ {% endif %}
75
+ </p>
76
+ <p><strong>Telegram:</strong>
77
+ {% if status.telegram_authenticated %}
78
+ <span class="badge bg-success">Connected</span>
79
+ <a href="{{ url_for('telegram_auth') }}" class="btn btn-sm btn-outline-primary">Re-authenticate</a>
80
+ {% else %}
81
+ <span class="badge bg-warning text-dark">Not Connected</span>
82
+ <a href="{{ url_for('telegram_auth') }}" class="btn btn-sm btn-primary">Connect</a>
83
+ {% endif %}
84
+ </p>
85
+ </div>
86
+ </div>
87
+
88
+ <div class="card status-card">
89
+ <div class="card-header">
90
+ <h5>Processing Control</h5>
91
+ </div>
92
+ <div class="card-body">
93
+ <form id="startForm">
94
+ <div class="mb-3">
95
+ <label for="limit" class="form-label">Batch Size (Videos per batch)</label>
96
+ <input type="number" class="form-control" id="limit" name="limit" value="5" min="1" max="50">
97
+ </div>
98
+ <button type="submit" class="btn btn-success" id="startBtn" {% if status.is_running %}disabled{% endif %}>Start Processing</button>
99
+ <button type="button" class="btn btn-danger" id="stopBtn" {% if not status.is_running %}disabled{% endif %}>Stop Processing</button>
100
+ </form>
101
+ </div>
102
+ </div>
103
+ </div>
104
+
105
+ <div class="col-md-6">
106
+ <div class="card status-card">
107
+ <div class="card-header">
108
+ <h5>Current Status</h5>
109
+ </div>
110
+ <div class="card-body">
111
+ <p><strong>Running:</strong>
112
+ {% if status.is_running %}
113
+ <span class="badge bg-success">Yes</span>
114
+ {% else %}
115
+ <span class="badge bg-secondary">No</span>
116
+ {% endif %}
117
+ </p>
118
+ <p><strong>Current Batch:</strong> {{ status.current_batch }}</p>
119
+ <p><strong>Processed in Session:</strong> {{ status.processed_count }}</p>
120
+ <p><strong>Failed in Session:</strong> {{ status.failed_count }}</p>
121
+ <p><strong>Total Processed (Ever):</strong> {{ status.total_processed }}</p>
122
+ <div id="confirmationArea">
123
+ <!-- Confirmation prompt will be inserted here by JavaScript -->
124
+ </div>
125
+ </div>
126
+ </div>
127
+ </div>
128
+ </div>
129
+
130
+ <div class="row mt-4">
131
+ <div class="col-12">
132
+ <div class="card">
133
+ <div class="card-header d-flex justify-content-between align-items-center">
134
+ <h5>Processing Logs</h5>
135
+ <button class="btn btn-sm btn-outline-secondary" id="clearLogsBtn">Clear Logs</button>
136
+ </div>
137
+ <div class="card-body">
138
+ <div class="log-container" id="logContainer">
139
+ <!-- Logs will be populated here by JavaScript -->
140
+ </div>
141
+ </div>
142
+ </div>
143
+ </div>
144
+ </div>
145
+ {% endblock %}
146
+
147
+ {% block scripts %}
148
+ <script>
149
+ // --- Configuration Saving Logic ---
150
+ document.getElementById('configForm').addEventListener('submit', function(e) {
151
+ e.preventDefault();
152
+ const saveBtn = document.getElementById('saveConfigBtn');
153
+ const statusDiv = document.getElementById('configSaveStatus');
154
+ const originalBtnText = saveBtn.textContent;
155
+
156
+ saveBtn.disabled = true;
157
+ saveBtn.textContent = 'Saving...';
158
+ statusDiv.innerHTML = '<div class="spinner-border spinner-border-sm" role="status"><span class="visually-hidden">Saving...</span></div>';
159
+
160
+ const formData = new FormData(this);
161
+
162
+ fetch('/save_config', {
163
+ method: 'POST',
164
+ body: formData
165
+ })
166
+ .then(response => {
167
+ if (!response.ok) {
168
+ return response.json().then(err => { throw new Error(err.message || 'Network response was not ok'); });
169
+ }
170
+ return response.json();
171
+ })
172
+ .then(data => {
173
+ if (data.success) {
174
+ statusDiv.innerHTML = '<div class="alert alert-success alert-dismissible fade show" role="alert">Configuration saved successfully!<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button></div>';
175
+ // Optionally, re-fetch status to update any dependent UI elements if needed
176
+ // updateStatus(); // If you add config status checks to the main status endpoint
177
+ } else {
178
+ statusDiv.innerHTML = `<div class="alert alert-danger alert-dismissible fade show" role="alert">Error saving configuration: ${data.message}<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button></div>`;
179
+ }
180
+ })
181
+ .catch(error => {
182
+ console.error('Error:', error);
183
+ statusDiv.innerHTML = `<div class="alert alert-danger alert-dismissible fade show" role="alert">Error saving configuration: ${error.message}<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button></div>`;
184
+ })
185
+ .finally(() => {
186
+ saveBtn.disabled = false;
187
+ saveBtn.textContent = originalBtnText;
188
+ // Auto-hide success message after a few seconds
189
+ const successAlert = statusDiv.querySelector('.alert-success');
190
+ if (successAlert) {
191
+ setTimeout(() => {
192
+ if (successAlert.parentNode === statusDiv) { // Check if still present
193
+ successAlert.remove();
194
+ }
195
+ }, 3000);
196
+ }
197
+ });
198
+ });
199
+
200
+ // --- Existing Status and Control Logic ---
201
+ let isWaitingForConfirmation = false;
202
+
203
+ function updateStatus() {
204
+ fetch('/status')
205
+ .then(response => response.json())
206
+ .then(data => {
207
+ // Update status indicators
208
+ document.getElementById('startBtn').disabled = data.is_running;
209
+ document.getElementById('stopBtn').disabled = !data.is_running;
210
+
211
+ // Update log container
212
+ const logContainer = document.getElementById('logContainer');
213
+ logContainer.innerHTML = '';
214
+ data.logs.forEach(log => {
215
+ const logEntry = document.createElement('div');
216
+ logEntry.textContent = `[${log.timestamp}] ${log.level}: ${log.message}`;
217
+ logEntry.classList.add('mb-1');
218
+ if (log.level === 'ERROR') logEntry.classList.add('text-danger');
219
+ else if (log.level === 'WARNING') logEntry.classList.add('text-warning');
220
+ logContainer.appendChild(logEntry);
221
+ });
222
+ logContainer.scrollTop = logContainer.scrollHeight; // Auto-scroll to bottom
223
+
224
+ // Handle confirmation prompt
225
+ const confirmationArea = document.getElementById('confirmationArea');
226
+ if (data.waiting_for_confirmation && !isWaitingForConfirmation) {
227
+ isWaitingForConfirmation = true;
228
+ confirmationArea.innerHTML = `
229
+ <div class="alert alert-info confirmation-prompt" role="alert">
230
+ <strong>Confirmation Needed:</strong> ${data.confirmation_message}
231
+ <div class="mt-2">
232
+ <button class="btn btn-sm btn-success confirm-btn" data-action="continue">Continue</button>
233
+ <button class="btn btn-sm btn-secondary confirm-btn" data-action="stop">Stop</button>
234
+ </div>
235
+ </div>
236
+ `;
237
+ } else if (!data.waiting_for_confirmation && isWaitingForConfirmation) {
238
+ isWaitingForConfirmation = false;
239
+ confirmationArea.innerHTML = ''; // Clear prompt
240
+ }
241
+
242
+ })
243
+ .catch(error => console.error('Error fetching status:', error));
244
+ }
245
+
246
+ // Initial update
247
+ updateStatus();
248
+ // Update status every 2 seconds
249
+ const statusInterval = setInterval(updateStatus, 2000);
250
+
251
+ // Event Listeners for Processing Control
252
+ document.getElementById('startForm').addEventListener('submit', function(e) {
253
+ e.preventDefault();
254
+ const formData = new FormData(this);
255
+ fetch('/start_processing', {
256
+ method: 'POST',
257
+ body: formData
258
+ })
259
+ .then(response => response.json())
260
+ .then(data => {
261
+ if (data.success) {
262
+ // updateStatus(); // Will be updated by interval
263
+ // Provide immediate feedback
264
+ const startBtn = document.getElementById('startBtn');
265
+ startBtn.disabled = true;
266
+ } else {
267
+ alert(data.message);
268
+ }
269
+ })
270
+ .catch(error => console.error('Error starting processing:', error));
271
+ });
272
+
273
+ document.getElementById('stopBtn').addEventListener('click', function() {
274
+ fetch('/stop_processing', {
275
+ method: 'POST'
276
+ })
277
+ .then(response => response.json())
278
+ .then(data => {
279
+ if (data.success) {
280
+ // updateStatus(); // Will be updated by interval
281
+ // Provide immediate feedback
282
+ const stopBtn = document.getElementById('stopBtn');
283
+ stopBtn.disabled = true;
284
+ } else {
285
+ alert(data.message);
286
+ }
287
+ })
288
+ .catch(error => console.error('Error stopping processing:', error));
289
+ });
290
+
291
+ document.getElementById('clearLogsBtn').addEventListener('click', function() {
292
+ fetch('/clear_logs', {
293
+ method: 'POST'
294
+ })
295
+ .then(response => response.json())
296
+ .then(data => {
297
+ if (data.success) {
298
+ const logContainer = document.getElementById('logContainer');
299
+ logContainer.innerHTML = '';
300
+ }
301
+ })
302
+ .catch(error => console.error('Error clearing logs:', error));
303
+ });
304
+
305
+ // Confirmation buttons (using event delegation)
306
+ document.addEventListener('click', function(e) {
307
+ if (e.target.classList.contains('confirm-btn')) {
308
+ const action = e.target.getAttribute('data-action');
309
+ fetch('/batch_confirmation', {
310
+ method: 'POST',
311
+ headers: {
312
+ 'Content-Type': 'application/x-www-form-urlencoded',
313
+ },
314
+ body: `action=${action}`
315
+ })
316
+ .then(response => response.json())
317
+ .then(data => {
318
+ if (data.success) {
319
+ // updateStatus(); // Will be updated by interval
320
+ // Optionally, clear the prompt immediately
321
+ document.getElementById('confirmationArea').innerHTML = '';
322
+ isWaitingForConfirmation = false;
323
+ } else {
324
+ alert(data.message);
325
+ }
326
+ })
327
+ .catch(error => console.error('Error sending confirmation:', error));
328
+ }
329
+ });
330
+
331
+ // Clear interval on page unload (optional, good practice)
332
+ window.addEventListener('beforeunload', () => {
333
+ clearInterval(statusInterval);
334
+ });
335
+
336
+ </script>
337
+ {% endblock %}