| | from flask import Flask, request, jsonify, render_template |
| | from flask_socketio import SocketIO, emit |
| | import json |
| | import uuid |
| | import os |
| | from datetime import datetime |
| |
|
| | app = Flask(__name__) |
| | app.config['SECRET_KEY'] = 'your-secret-key' |
| | socketio = SocketIO(app) |
| |
|
| | |
| | jobs = [] |
| | connected_clients = {} |
| |
|
| | def save_jobs(): |
| | """Save jobs to JSON file""" |
| | try: |
| | with open('jobs.json', 'w') as f: |
| | json.dump(jobs, f) |
| | except Exception as e: |
| | print(f"Error saving jobs: {e}") |
| |
|
| | def load_jobs(): |
| | """Load jobs from JSON file""" |
| | try: |
| | if os.path.exists('jobs.json') and os.path.getsize('jobs.json') > 0: |
| | with open('jobs.json', 'r') as f: |
| | return json.load(f) |
| | except Exception as e: |
| | print(f"Error loading jobs: {e}") |
| | return [] |
| |
|
| | @app.route('/') |
| | def index(): |
| | return render_template('index.html') |
| |
|
| | @app.route('/submit_job', methods=['POST']) |
| | def submit_job(): |
| | youtube_url = request.form.get("youtube_url") |
| | exit_time = int(request.form.get("exit_time", 10)) |
| | mode = request.form.get("mode", "once") |
| | job_id = str(uuid.uuid4()) |
| |
|
| | job = { |
| | "job_id": job_id, |
| | "youtube_url": youtube_url, |
| | "exit_time": exit_time, |
| | "mode": mode, |
| | "status": "pending" |
| | } |
| | jobs.append(job) |
| | save_jobs() |
| |
|
| | return jsonify({"message": "Job submitted successfully", "job_id": job_id}) |
| |
|
| | @socketio.on('client_info') |
| | def handle_client_info(data): |
| | client_id = request.sid |
| | connected_clients[client_id] = { |
| | 'ip': data.get('ip', 'Unknown'), |
| | 'country': data.get('country', 'Unknown'), |
| | 'city': data.get('city', 'Unknown'), |
| | 'connected_at': datetime.now().isoformat() |
| | } |
| | |
| | emit('client_connected', { |
| | 'client_id': client_id, |
| | 'ip': data.get('ip', 'Unknown'), |
| | 'country': data.get('country', 'Unknown'), |
| | 'city': data.get('city', 'Unknown') |
| | }, broadcast=True) |
| |
|
| | @socketio.on('disconnect') |
| | def handle_disconnect(): |
| | client_id = request.sid |
| | if client_id in connected_clients: |
| | del connected_clients[client_id] |
| | emit('client_disconnected', client_id, broadcast=True) |
| |
|
| | @app.route('/get_job', methods=['GET']) |
| | def get_job(): |
| | for job in jobs: |
| | if job["status"] == "pending": |
| | return jsonify(job) |
| | return jsonify({"message": "No pending jobs"}) |
| |
|
| | @app.route('/update_job_status', methods=['POST']) |
| | def update_job_status(): |
| | data = request.get_json() |
| | job_id = data.get("job_id") |
| | status = data.get("status", "done") |
| |
|
| | for job in jobs: |
| | if job["job_id"] == job_id: |
| | job["status"] = status |
| | break |
| |
|
| | save_jobs() |
| | return jsonify({"message": f"Job {job_id} marked as {status}"}) |
| |
|
| | if __name__ == "__main__": |
| | jobs = load_jobs() |
| | |
| | if not os.path.exists('templates'): |
| | os.makedirs('templates') |
| | |
| | template_path = os.path.join('templates', 'index.html') |
| | if not os.path.exists(template_path): |
| | with open(template_path, 'w') as f: |
| | f.write('''<!DOCTYPE html> |
| | <html lang="en"> |
| | <head> |
| | <meta charset="UTF-8"> |
| | <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| | <title>YouTube Job Manager</title> |
| | <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet"> |
| | <style> |
| | body { padding: 20px; } |
| | .connection-status { margin-bottom: 20px; } |
| | </style> |
| | </head> |
| | <body> |
| | <div class="container"> |
| | <div class="row"> |
| | <div class="col-md-8 offset-md-2"> |
| | <h1 class="mb-4">YouTube Job Manager</h1> |
| | |
| | <div class="card mb-4"> |
| | <div class="card-header"> |
| | <h5 class="mb-0">Connected Clients</h5> |
| | </div> |
| | <div class="card-body"> |
| | <table class="table" id="clientsTable"> |
| | <thead> |
| | <tr> |
| | <th>IP Address</th> |
| | <th>Country</th> |
| | <th>City</th> |
| | <th>Status</th> |
| | </tr> |
| | </thead> |
| | <tbody id="clientsTableBody"> |
| | </tbody> |
| | </table> |
| | </div> |
| | </div> |
| | |
| | <div class="card"> |
| | <div class="card-header"> |
| | <h5 class="mb-0">Submit New Job</h5> |
| | </div> |
| | <div class="card-body"> |
| | <form action="/submit_job" method="post" class="needs-validation" novalidate> |
| | <div class="mb-3"> |
| | <label for="youtube_url" class="form-label">YouTube URL:</label> |
| | <input type="text" class="form-control" id="youtube_url" name="youtube_url" required> |
| | </div> |
| | |
| | <div class="mb-3"> |
| | <label for="exit_time" class="form-label">Exit Time (seconds):</label> |
| | <input type="number" class="form-control" id="exit_time" name="exit_time" value="10" required> |
| | </div> |
| | |
| | <div class="mb-3"> |
| | <label for="mode" class="form-label">Mode:</label> |
| | <select class="form-select" id="mode" name="mode"> |
| | <option value="once">Once</option> |
| | <option value="again">Again</option> |
| | <option value="continuous">Continuous</option> |
| | </select> |
| | </div> |
| | |
| | <button type="submit" class="btn btn-primary">Submit Job</button> |
| | </form> |
| | </div> |
| | </div> |
| | </div> |
| | </div> |
| | </div> |
| | |
| | <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script> |
| | <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.0.1/socket.io.js"></script> |
| | <script> |
| | const socket = io(); |
| | |
| | socket.on('connect', () => { |
| | console.log('Connected to server'); |
| | }); |
| | |
| | socket.on('client_connected', (data) => { |
| | const tableBody = document.getElementById('clientsTableBody'); |
| | const row = document.createElement('tr'); |
| | row.setAttribute('data-client-id', data.client_id); |
| | row.innerHTML = ` |
| | <td>${data.ip}</td> |
| | <td>${data.country}</td> |
| | <td>${data.city}</td> |
| | <td><span class="badge bg-success">Connected</span></td> |
| | `; |
| | tableBody.appendChild(row); |
| | }); |
| | |
| | socket.on('client_disconnected', (clientId) => { |
| | const row = document.querySelector(`tr[data-client-id="${clientId}"]`); |
| | if (row) { |
| | row.querySelector('.badge').className = 'badge bg-danger'; |
| | row.querySelector('.badge').textContent = 'Disconnected'; |
| | } |
| | }); |
| | </script> |
| | </body> |
| | </html> |
| | ''') |
| | |
| | socketio.run(app, host="0.0.0.0", port=7860, debug=False, allow_unsafe_werkzeug=True) |
| |
|
| |
|