modified: app.py

modified:   templates/quiz.html
This commit is contained in:
Simon
2025-09-20 23:00:40 +02:00
parent 9e33eeb526
commit 017136d19c
2 changed files with 115 additions and 360 deletions

105
app.py
View File

@@ -10,9 +10,6 @@ import random
from difflib import SequenceMatcher
import re
import json
import unicodedata
import secrets
from datetime import datetime, timedelta
app = Flask(__name__)
app.secret_key = os.getenv("SECRET_KEY")
@@ -37,13 +34,15 @@ def get_translations():
def get_spotify_client():
token_info = session.get("token_info", None)
if not token_info:
# Kein Token, redirect handled elsewhere
return None
# Prüfen, ob Token abgelaufen ist
sp_oauth = SpotifyOAuth(
client_id=os.getenv("SPOTIPY_CLIENT_ID"),
client_secret=os.getenv("SPOTIPY_CLIENT_SECRET"),
redirect_uri=os.getenv("SPOTIPY_REDIRECT_URI"),
scope=SCOPE,
cache_path=None # <--- wichtig!
cache_path=".cache"
)
if sp_oauth.is_token_expired(token_info):
token_info = sp_oauth.refresh_access_token(token_info['refresh_token'])
@@ -54,20 +53,13 @@ def similarity(a, b):
return SequenceMatcher(None, a.lower(), b.lower()).ratio()
def clean_title(title):
# Unicode-Normalisierung (z.B. é -> e)
title = unicodedata.normalize('NFKD', title)
title = "".join([c for c in title if not unicodedata.combining(c)])
# Entfernt alles in () oder []
title = re.sub(r"(\s*[\(\[][^)\]]*[\)\]])", "", title)
# Vereinheitliche Apostrophen und Anführungszeichen
title = title.replace("", "'").replace("", "'").replace("`", "'")
title = title.replace('"', '').replace("", '').replace("", '').replace("", '')
title = title.replace("'", "")
# Entferne alle nicht-alphanumerischen Zeichen (außer Leerzeichen)
title = re.sub(r"[^a-zA-Z0-9äöüÄÖÜß ]", "", title)
# Mehrfache Leerzeichen zu einem
title = re.sub(r"\s+", " ", title)
return title.strip().lower()
title = title.replace("'", "") # Optional: alle Apostrophen entfernen
return title.strip()
def get_all_playlist_tracks(sp, playlist_id):
tracks = []
@@ -117,28 +109,17 @@ def callback():
user = sp.current_user()
session["user"] = user
# Setze ein 30-Tage-Cookie mit Userdaten (ohne Token!)
resp = redirect("/playlists")
user_cookie = json.dumps({
"id": user.get("id"),
"display_name": user.get("display_name"),
"email": user.get("email"),
"images": user.get("images"),
})
resp.set_cookie("quizify_user", user_cookie, max_age=60*60*24*30, httponly=True, samesite="Lax")
return resp
return redirect("/playlists")
@app.route("/playlists")
def playlists():
sp = get_spotify_client()
playlists = sp.current_user_playlists()["items"]
user = get_user_from_cookie()
return render_template("playlists.html", playlists=playlists, translations=get_translations(), user=user)
return render_template("playlists.html", playlists=playlists, translations=get_translations())
@app.route("/quiz/<playlist_id>")
def quiz(playlist_id):
game_mode = request.args.get('mode', 'artist')
is_multiplayer = request.args.get('local_multiplayer') == '1'
sp = get_spotify_client()
tracks = get_all_playlist_tracks(sp, playlist_id)
@@ -180,10 +161,8 @@ def quiz(playlist_id):
}
all_tracks.append(track_info)
user = get_user_from_cookie()
template = "quiz_multiplayer.html" if is_multiplayer else "quiz_single.html"
return render_template(
template,
"quiz.html",
track=track,
access_token=access_token,
playlist_id=playlist_id,
@@ -193,8 +172,7 @@ def quiz(playlist_id):
total_questions=len(tracks),
score=score,
answered=answered,
translations=get_translations(),
user=user
translations=get_translations()
)
@app.route("/search_track", methods=["POST"])
@@ -237,8 +215,7 @@ def check_answer():
game_mode = data.get('game_mode', 'artist')
playlist_id = data.get('playlist_id')
# Immer clean_title für title und artist
if game_mode in ['title', 'artist']:
if game_mode == 'title':
guess = clean_title(guess)
correct_answer = clean_title(correct_answer)
@@ -267,13 +244,7 @@ def play_track():
return {"error": "Missing device_id or track_uri"}, 400
sp = get_spotify_client()
try:
sp.start_playback(device_id=device_id, uris=[track_uri], position_ms=position_ms)
except spotipy.exceptions.SpotifyException as e:
if "Device not found" in str(e):
# Spezieller Fehlercode, damit das Frontend weiß, dass es neu laden soll
return {"error": "device_not_found", "message": "Spotify-Player nicht gefunden. Die Seite wird neu geladen..."}, 409
return {"error": str(e)}, 500
sp.start_playback(device_id=device_id, uris=[track_uri], position_ms=position_ms)
return {"success": True}
@@ -302,9 +273,12 @@ def toggle_playback():
@app.route('/logout')
def logout():
session.clear()
resp = redirect(url_for('home'))
resp.set_cookie("quizify_user", "", expires=0)
return resp
return redirect(url_for('home'))
@app.route('/')
def index():
user = session.get('user') # Benutzerinfos aus der Session holen, falls vorhanden
return render_template('index.html', user=user, translations=get_translations())
@app.route("/reset_quiz/<playlist_id>")
def reset_quiz(playlist_id):
@@ -315,50 +289,5 @@ def reset_quiz(playlist_id):
return redirect(url_for('quiz', playlist_id=playlist_id, mode=next_mode))
return redirect(url_for('playlists'))
@app.route("/gamemodes/<playlist_id>")
def gamemodes(playlist_id):
return render_template("gamemodes.html", playlist_id=playlist_id, translations=get_translations())
invites = {} # {token: expiry_datetime}
@app.route("/invite")
def invite():
duration = int(request.args.get("duration", 60)) # Minuten
token = secrets.token_urlsafe(16)
expires = datetime.utcnow() + timedelta(minutes=duration)
invites[token] = expires
invite_link = url_for('guest_join', token=token, _external=True)
# Gib nur den Link als Klartext zurück!
return invite_link
@app.route("/invite/<token>")
def guest_join(token):
expires = invites.get(token)
if not expires or expires < datetime.utcnow():
return "Invite link expired or invalid.", 403
# Setze ein Cookie, damit der Gast als eingeladener User erkannt wird (optional)
resp = redirect(url_for('login'))
resp.set_cookie("guest_token", token, max_age=60*60) # 1 Stunde gültig
return resp
def get_user_from_cookie():
user_cookie = request.cookies.get("quizify_user")
if user_cookie:
try:
return json.loads(user_cookie)
except Exception:
return None
return None
@app.route("/playerselect/<playlist_id>")
def playerselect(playlist_id):
game_mode = request.args.get('mode', 'artist')
return render_template(
"playerselect.html",
playlist_id=playlist_id,
game_mode=game_mode,
translations=get_translations()
)
if __name__ == "__main__":
app.run(host="0.0.0.0", port=5000, debug=True)