#!/usr/bin/env python3 # -*- coding: utf-8 -*- """Flask web server for the song projector application. HTML, CSS and JavaScript are now stored in separate template/static files rather than being generated inside Python code. """ import threading import os from flask import Flask, render_template, request, jsonify # create Flask app with proper folders relative to this file app = Flask(__name__, static_folder="static", template_folder="templates") # Globalna referenca na SongProjector aplikaciju _projector_app = None def set_projector_app(projector_app): """Postavi referenco na SongProjector aplikacijo""" global _projector_app _projector_app = projector_app # ---------------------------------------------------------- # Flask rute # ---------------------------------------------------------- @app.route("/", methods=["GET"]) def index(): """Prikaže glavno HTML stranko""" # predaja nadzora Jinja2, ki poišče web/templates/index.html return render_template("index.html") @app.route('/api/state', methods=['GET']) def get_state(): """Vrati trenutno stanje aplikacije""" if _projector_app is None: return jsonify({ 'current_text': 'Napaka: Aplikacija ni inicijalizirana', 'page_info': '', 'caps_mode': False, 'can_prev': False, 'can_next': False }) current_text = "" page_info = "" can_prev = False can_next = False if _projector_app.pages: current_text = _projector_app.pages[_projector_app.current_page_index] if _projector_app.all_caps_mode: current_text = current_text.upper() current_page = _projector_app.current_page_index + 1 total_pages = len(_projector_app.pages) page_info = f"{_projector_app.song_number_last} {current_page}/{total_pages}" can_prev = _projector_app.current_page_index > 0 can_next = _projector_app.current_page_index + 1 < len(_projector_app.pages) else: current_text = "Pripravljeno. Vpiši številko pesmi ali drugega besedila." return jsonify({ 'current_text': current_text, 'page_info': page_info, 'caps_mode': _projector_app.all_caps_mode, 'can_prev': can_prev, 'can_next': can_next }) @app.route('/api/load_song', methods=['POST']) def load_song(): """Naloži pesem po številki""" if _projector_app is None: return jsonify({'status': 'error', 'message': 'Aplikacija ni inicijalizirana'}) data = request.get_json() song_number = data.get('song_number', '').strip() if song_number: _projector_app.song_number = song_number _projector_app.load_song() if hasattr(_projector_app, 'show_page'): _projector_app.show_page() return jsonify({'status': 'ok'}) @app.route('/api/next_page', methods=['POST']) def next_page(): """Naslednja stran""" if _projector_app is not None: _projector_app.next_page() return jsonify({'status': 'ok'}) @app.route('/api/prev_page', methods=['POST']) def prev_page(): """Prejšnja stran""" if _projector_app is not None: _projector_app.prev_page() return jsonify({'status': 'ok'}) @app.route('/api/clear_screen', methods=['POST']) def clear_screen(): """Zatemniti ekran""" if _projector_app is not None: _projector_app.clear_screen() return jsonify({'status': 'ok'}) @app.route('/api/toggle_caps', methods=['POST']) def toggle_caps(): """Preklop med velikimi in malimi črkami""" if _projector_app is not None: _projector_app.all_caps_mode = not _projector_app.all_caps_mode _projector_app.show_page() return jsonify({'status': 'ok'}) @app.route('/api/search_songs', methods=['POST']) def search_songs(): """Iskanje besedil po naslovu""" if _projector_app is None: return jsonify({'results': []}) data = request.get_json() query = data.get('query', '').strip() if not query: return jsonify({'results': []}) try: _projector_app.cursor.execute( "SELECT id, title FROM songs WHERE title LIKE ? COLLATE NOCASE", (f"%{query}%",) ) results = _projector_app.cursor.fetchall() return jsonify({'results': results}) except Exception as e: return jsonify({'error': str(e)}) @app.route('/api/get_song_details', methods=['GET']) def get_song_details(): """Vrne podrobnosti trenutno naložene pesmi ali pesmi po ID-ju""" if _projector_app is None: return jsonify({'status': 'error', 'message': 'Aplikacija ni inicijalizirana'}) song_id_param = request.args.get('id') if song_id_param: try: song_id = int(song_id_param) except ValueError: return jsonify({'status': 'error', 'message': 'Neveljaven ID pesmi'}) elif _projector_app.song_number_last: try: song_id = int(_projector_app.song_number_last) except ValueError: return jsonify({'status': 'error', 'message': 'Neveljavna številka naložene pesmi'}) else: return jsonify({'status': 'error', 'message': 'Nobena pesem ni naložena'}) try: _projector_app.cursor.execute("SELECT id, title, lyrics FROM songs WHERE id=?", (song_id,)) result = _projector_app.cursor.fetchone() if result: return jsonify({ 'status': 'ok', 'song': { 'id': result[0], 'title': result[1], 'lyrics': result[2] } }) else: return jsonify({'status': 'error', 'message': f'Pesem s številko {song_id} ni najdena v bazi'}) except Exception as e: return jsonify({'status': 'error', 'message': str(e)}) @app.route('/api/update_song', methods=['POST']) def update_song(): """Posodobi naslov in besedilo pesmi ali ustvari novo""" if _projector_app is None: return jsonify({'status': 'error', 'message': 'Aplikacija ni inicijalizirana'}) data = request.get_json() song_id = data.get('id') title = data.get('title', '').strip() lyrics = data.get('lyrics', '').strip() if not song_id or not title or not lyrics: return jsonify({'status': 'error', 'message': 'Manjkajoči podatki'}) try: if song_id == 'new': # Pridobi prvo naslednjo prosto številko _projector_app.cursor.execute("SELECT MAX(id) FROM songs") max_id = _projector_app.cursor.fetchone()[0] new_id = (max_id or 0) + 1 _projector_app.cursor.execute( "INSERT INTO songs (id, title, lyrics) VALUES (?, ?, ?)", (new_id, title, lyrics) ) _projector_app.conn.commit() # Naloži novo pesem _projector_app.song_number = str(new_id) _projector_app.load_song() return jsonify({'status': 'ok', 'new_id': new_id}) # Obstoječa pesem _projector_app.cursor.execute("UPDATE songs SET title=?, lyrics=? WHERE id=?", (title, lyrics, song_id)) _projector_app.conn.commit() # Osvežimo trenutno pesem, če je to ta, ki smo jo pravkar uredili if str(_projector_app.song_number_last) == str(song_id): _projector_app.song_number = str(song_id) _projector_app.load_song() return jsonify({'status': 'ok'}) except Exception as e: return jsonify({'status': 'error', 'message': str(e)}) def run_server(host='127.0.0.1', port=5000): """Zaženi Flask server""" app.run(host=host, port=port, debug=False, use_reloader=False, threaded=True) def start_server_thread(projector_app, host='127.0.0.1', port=5000): """Zaženi server v zvjenoj niti""" set_projector_app(projector_app) server_thread = threading.Thread( target=run_server, args=(host, port), daemon=True ) server_thread.start() return server_thread