diff --git a/server.py b/server.py deleted file mode 100644 index 4de2674..0000000 --- a/server.py +++ /dev/null @@ -1,244 +0,0 @@ -# Monkey patch MUST be first - before any other imports! -import eventlet -eventlet.monkey_patch() - -import os -from flask import Flask, send_from_directory, jsonify, request, session -from flask_socketio import SocketIO, emit -from dotenv import load_dotenv -# Load environment variables from .env file -load_dotenv() -import downloader - -# Relay State -broadcast_state = { - 'active': False -} -listener_count = 0 -MUSIC_FOLDER = "music" -# Ensure music folder exists -if not os.path.exists(MUSIC_FOLDER): - os.makedirs(MUSIC_FOLDER) - -# Helper for shared routes -def setup_shared_routes(app): - @app.route('/library.json') - def get_library(): - library = [] - if os.path.exists(MUSIC_FOLDER): - for filename in sorted(os.listdir(MUSIC_FOLDER)): - if filename.lower().endswith(('.mp3', '.m4a', '.wav', '.flac', '.ogg')): - library.append({ - "title": os.path.splitext(filename)[0], - "file": f"music/{filename}" - }) - return jsonify(library) - - @app.route('/download', methods=['POST']) - def download(): - data = request.json - url = data.get('url') - quality = data.get('quality', '320') - if not url: - return jsonify({"success": False, "error": "No URL provided"}), 400 - result = downloader.download_mp3(url, quality) - return jsonify(result) - - @app.route('/search_youtube', methods=['GET']) - def search_youtube(): - query = request.args.get('q', '') - if not query: - return jsonify({"success": False, "error": "No query provided"}), 400 - - # Get API key from environment variable - api_key = os.environ.get('YOUTUBE_API_KEY', '') - if not api_key: - return jsonify({ - "success": False, - "error": "YouTube API key not configured. Set YOUTUBE_API_KEY environment variable." - }), 500 - - try: - import requests - # Search YouTube using Data API v3 - url = 'https://www.googleapis.com/youtube/v3/search' - params = { - 'part': 'snippet', - 'q': query, - 'type': 'video', - 'videoCategoryId': '10', # Music category - 'maxResults': 20, - 'key': api_key - } - - response = requests.get(url, params=params) - data = response.json() - - if 'error' in data: - return jsonify({ - "success": False, - "error": data['error'].get('message', 'YouTube API error') - }), 400 - - # Format results - results = [] - for item in data.get('items', []): - results.append({ - 'videoId': item['id']['videoId'], - 'title': item['snippet']['title'], - 'channel': item['snippet']['channelTitle'], - 'thumbnail': item['snippet']['thumbnails']['medium']['url'], - 'url': f"https://www.youtube.com/watch?v={item['id']['videoId']}" - }) - - return jsonify({"success": True, "results": results}) - - except Exception as e: - return jsonify({"success": False, "error": str(e)}), 500 - - @app.route('/') - def serve_static(filename): - response = send_from_directory('.', filename) - if filename.endswith(('.css', '.js', '.html')): - response.headers['Cache-Control'] = 'no-store, no-cache, must-revalidate, max-age=0' - return response - - @app.route('/') - def index(): - return send_from_directory('.', 'index.html') - -# === DJ SERVER (Port 5000) === -dj_app = Flask(__name__, static_folder='.', static_url_path='') -dj_app.config['SECRET_KEY'] = 'dj_panel_secret' -setup_shared_routes(dj_app) -dj_socketio = SocketIO( - dj_app, - cors_allowed_origins="*", - async_mode='eventlet', - max_http_buffer_size=1e8, # 100MB buffer - ping_timeout=10, - ping_interval=5, - logger=False, - engineio_logger=False -) - -@dj_socketio.on('connect') -def dj_connect(): - print(f"🎧 DJ connected: {request.sid}") - session['is_dj'] = True - -@dj_socketio.on('disconnect') -def dj_disconnect(): - if session.get('is_dj'): - print("⚠️ DJ disconnected - broadcast will continue until manually stopped") - session['is_dj'] = False - # Don't stop streaming_active - let it continue - # Broadcast will resume when DJ reconnects - -def stop_broadcast_after_timeout(): - """No longer used - broadcasts don't auto-stop""" - pass - -@dj_socketio.on('start_broadcast') -def dj_start(): - broadcast_state['active'] = True - session['is_dj'] = True - print("🎙️ Broadcast -> ACTIVE") - - listener_socketio.emit('broadcast_started', namespace='/') - listener_socketio.emit('stream_status', {'active': True}, namespace='/') - -@dj_socketio.on('stop_broadcast') -def dj_stop(): - broadcast_state['active'] = False - session['is_dj'] = False - print("🛑 DJ stopped broadcasting") - - listener_socketio.emit('broadcast_stopped', namespace='/') - listener_socketio.emit('stream_status', {'active': False}, namespace='/') - -@dj_socketio.on('audio_chunk') -def dj_audio(data): - # Relay audio chunk to all listeners immediately - if broadcast_state['active']: - listener_socketio.emit('audio_data', data, namespace='/') - -# === LISTENER SERVER (Port 6000) === -listener_app = Flask(__name__, static_folder='.', static_url_path='') -listener_app.config['SECRET_KEY'] = 'listener_secret' -setup_shared_routes(listener_app) -listener_socketio = SocketIO( - listener_app, - cors_allowed_origins="*", - async_mode='eventlet', - max_http_buffer_size=1e8, # 100MB buffer - ping_timeout=10, - ping_interval=5, - logger=False, - engineio_logger=False -) - -@listener_socketio.on('connect') -def listener_connect(): - print(f"👂 Listener Socket Connected: {request.sid}") - -@listener_socketio.on('disconnect') -def listener_disconnect(): - global listener_count - if session.get('is_listener'): - # Clear session flag FIRST to prevent re-entry issues - session['is_listener'] = False - listener_count = max(0, listener_count - 1) - print(f"❌ Listener left. Total: {listener_count}") - # Broadcast to all listeners - listener_socketio.emit('listener_count', {'count': listener_count}, namespace='/') - # Broadcast to all DJs - dj_socketio.emit('listener_count', {'count': listener_count}, namespace='/') - -@listener_socketio.on('join_listener') -def listener_join(): - global listener_count - if not session.get('is_listener'): - session['is_listener'] = True - listener_count += 1 - print(f"👂 New listener joined. Total: {listener_count}") - # Broadcast to all listeners - listener_socketio.emit('listener_count', {'count': listener_count}, namespace='/') - # Broadcast to all DJs - dj_socketio.emit('listener_count', {'count': listener_count}, namespace='/') - - emit('stream_status', {'active': broadcast_state['active']}) - -@listener_socketio.on('request_header') -def handle_request_header(): - # Header logic removed for local relay mode - pass - -@listener_socketio.on('get_listener_count') -def listener_get_count(): - emit('listener_count', {'count': listener_count}) - -# DJ Panel Routes (No engine commands needed in local mode) -@dj_socketio.on('get_mixer_status') -def get_mixer_status(): - pass - -@dj_socketio.on('audio_sync_queue') -def audio_sync_queue(data): - pass - - -if __name__ == '__main__': - print("=" * 50) - print("🎧 TECHDJ PRO - DUAL PORT ARCHITECTURE") - print("=" * 50) - print("👉 DJ PANEL: http://localhost:5000") - print("👉 LISTEN PAGE: http://localhost:5001") - print("=" * 50) - - # Audio engine DISABLED - print("✅ Local Radio server ready") - - # Run both servers using eventlet's spawn - eventlet.spawn(dj_socketio.run, dj_app, host='0.0.0.0', port=5000, debug=False) - listener_socketio.run(listener_app, host='0.0.0.0', port=5001, debug=False)