230 lines
6.8 KiB
Python
230 lines
6.8 KiB
Python
#!/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."
|
|
|
|
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 pesmi 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"""
|
|
if _projector_app is None:
|
|
return jsonify({'status': 'error', 'message': 'Aplikacija ni inicijalizirana'})
|
|
|
|
if not _projector_app.song_number_last:
|
|
return jsonify({'status': 'error', 'message': 'Nobena pesem ni naložena'})
|
|
|
|
try:
|
|
song_id = int(_projector_app.song_number_last)
|
|
_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': 'Pesem 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"""
|
|
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:
|
|
_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
|