257 lines
7.8 KiB
Python
257 lines
7.8 KiB
Python
__version__ = "pre-dev"
|
|
__all__ = ["quizify"]
|
|
__author__ = "SimolZimol"
|
|
|
|
from flask import Flask, redirect, request, session, url_for, render_template
|
|
import os
|
|
import spotipy
|
|
from spotipy.oauth2 import SpotifyOAuth
|
|
import random
|
|
from difflib import SequenceMatcher
|
|
import re
|
|
|
|
app = Flask(__name__)
|
|
app.secret_key = os.getenv("SECRET_KEY")
|
|
|
|
# Erweiterte Berechtigungen für Web Playback SDK
|
|
SCOPE = "user-library-read playlist-read-private streaming user-read-email user-read-private"
|
|
|
|
def get_spotify_client():
|
|
return spotipy.Spotify(auth_manager=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=".cache"
|
|
))
|
|
|
|
def similarity(a, b):
|
|
return SequenceMatcher(None, a.lower(), b.lower()).ratio()
|
|
|
|
def clean_title(title):
|
|
# Entfernt alles in () oder []
|
|
return re.sub(r"(\s*[\(\[][^)\]]*[\)\]])", "", title).strip()
|
|
|
|
def get_all_playlist_tracks(sp, playlist_id):
|
|
tracks = []
|
|
offset = 0
|
|
limit = 100
|
|
while True:
|
|
response = sp.playlist_items(playlist_id, additional_types=["track"], limit=limit, offset=offset)
|
|
items = response["items"]
|
|
if not items:
|
|
break
|
|
tracks.extend([item["track"] for item in items if item.get("track")])
|
|
if len(items) < limit:
|
|
break
|
|
offset += limit
|
|
return tracks
|
|
|
|
@app.route("/")
|
|
def home():
|
|
return render_template("login.html")
|
|
|
|
@app.route("/login")
|
|
def login():
|
|
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
|
|
)
|
|
auth_url = sp_oauth.get_authorize_url()
|
|
return redirect(auth_url)
|
|
|
|
@app.route("/callback")
|
|
def callback():
|
|
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
|
|
)
|
|
session.clear()
|
|
code = request.args.get('code')
|
|
token_info = sp_oauth.get_access_token(code)
|
|
session["token_info"] = token_info
|
|
return redirect("/playlists")
|
|
|
|
@app.route("/playlists")
|
|
def playlists():
|
|
sp = get_spotify_client()
|
|
playlists = sp.current_user_playlists()["items"]
|
|
return render_template("playlists.html", playlists=playlists)
|
|
|
|
@app.route("/quiz/<playlist_id>")
|
|
def quiz(playlist_id):
|
|
game_mode = request.args.get('mode', 'artist')
|
|
|
|
sp = get_spotify_client()
|
|
tracks = get_all_playlist_tracks(sp, playlist_id)
|
|
|
|
if not tracks:
|
|
return "Keine Tracks verfügbar!"
|
|
|
|
played_tracks = session.get(f'played_tracks_{playlist_id}', [])
|
|
score = session.get(f'score_{playlist_id}', 0)
|
|
|
|
# Wenn alle Songs gespielt wurden, played_tracks zurücksetzen, Score bleibt!
|
|
available_tracks = [t for t in tracks if t["id"] not in played_tracks]
|
|
if not available_tracks:
|
|
played_tracks = []
|
|
available_tracks = tracks
|
|
# Score NICHT zurücksetzen!
|
|
|
|
track = random.choice(available_tracks)
|
|
played_tracks.append(track["id"])
|
|
session[f'played_tracks_{playlist_id}'] = played_tracks
|
|
session[f'score_{playlist_id}'] = score
|
|
|
|
# Für die Anzeige der beantworteten Fragen
|
|
answered = len(played_tracks) - 1 if len(played_tracks) > 0 else 0
|
|
|
|
token_info = session.get('token_info', None)
|
|
if not token_info:
|
|
return redirect('/login')
|
|
access_token = token_info['access_token']
|
|
|
|
all_tracks = []
|
|
for item in tracks:
|
|
track_info = {
|
|
"id": item["id"],
|
|
"name": item["name"],
|
|
"artist": item["artists"][0]["name"],
|
|
"uri": item["uri"],
|
|
"release_date": item.get("album", {}).get("release_date", "Unbekannt")[:4]
|
|
}
|
|
all_tracks.append(track_info)
|
|
|
|
return render_template(
|
|
"quiz.html",
|
|
track=track,
|
|
access_token=access_token,
|
|
playlist_id=playlist_id,
|
|
game_mode=game_mode,
|
|
all_tracks=all_tracks,
|
|
question_number=len(played_tracks),
|
|
total_questions=len(tracks),
|
|
score=score,
|
|
answered=answered
|
|
)
|
|
|
|
@app.route("/search_track", methods=["POST"])
|
|
def search_track():
|
|
data = request.json
|
|
query = data.get('query', '').lower()
|
|
all_tracks = data.get('all_tracks', [])
|
|
|
|
if not query or not all_tracks:
|
|
return {"results": []}
|
|
|
|
# Suche nach bereinigtem Titel und Künstler
|
|
results = []
|
|
for track in all_tracks:
|
|
cleaned_name = clean_title(track["name"])
|
|
cleaned_query = clean_title(query)
|
|
name_similarity = similarity(cleaned_query, cleaned_name)
|
|
artist_similarity = similarity(query, track["artist"])
|
|
|
|
# Wenn Name oder Künstler zu 80% übereinstimmt
|
|
if name_similarity >= 0.8 or artist_similarity >= 0.8:
|
|
results.append({
|
|
"id": track["id"],
|
|
"name": track["name"],
|
|
"artist": track["artist"],
|
|
"uri": track["uri"],
|
|
"similarity": max(name_similarity, artist_similarity)
|
|
})
|
|
|
|
# Nach Ähnlichkeit sortieren
|
|
results.sort(key=lambda x: x["similarity"], reverse=True)
|
|
|
|
return {"results": results}
|
|
|
|
@app.route("/check_answer", methods=["POST"])
|
|
def check_answer():
|
|
data = request.json
|
|
guess = data.get('guess', '').lower()
|
|
correct_answer = data.get('correct_answer', '').lower()
|
|
game_mode = data.get('game_mode', 'artist')
|
|
playlist_id = data.get('playlist_id')
|
|
|
|
if game_mode == 'title':
|
|
guess = clean_title(guess)
|
|
correct_answer = clean_title(correct_answer)
|
|
|
|
if game_mode == 'year':
|
|
is_correct = guess == correct_answer
|
|
else:
|
|
is_correct = similarity(guess, correct_answer) >= 0.9
|
|
|
|
# Score erhöhen, wenn richtig
|
|
if is_correct and playlist_id:
|
|
key = f'score_{playlist_id}'
|
|
session[key] = session.get(key, 0) + 1
|
|
|
|
return {
|
|
"correct": is_correct,
|
|
"correct_answer": correct_answer
|
|
}
|
|
|
|
@app.route("/play_track", methods=["POST"])
|
|
def play_track():
|
|
device_id = request.args.get('device_id')
|
|
track_uri = request.args.get('track_uri')
|
|
|
|
if not device_id or not track_uri:
|
|
return {"error": "Missing device_id or track_uri"}, 400
|
|
|
|
sp = get_spotify_client()
|
|
sp.start_playback(device_id=device_id, uris=[track_uri])
|
|
|
|
return {"success": True}
|
|
|
|
@app.route("/toggle_playback", methods=["POST"])
|
|
def toggle_playback():
|
|
data = request.json
|
|
device_id = data.get('device_id')
|
|
|
|
if not device_id:
|
|
return {"error": "Missing device_id"}, 400
|
|
|
|
sp = get_spotify_client()
|
|
# Playback-Status für das richtige Gerät prüfen
|
|
current_playback = sp.current_playback()
|
|
is_playing = False
|
|
if current_playback and current_playback.get('device', {}).get('id') == device_id:
|
|
is_playing = current_playback.get('is_playing', False)
|
|
|
|
if is_playing:
|
|
sp.pause_playback(device_id=device_id)
|
|
else:
|
|
sp.start_playback(device_id=device_id)
|
|
|
|
return {"success": True}
|
|
|
|
@app.route('/logout')
|
|
def logout():
|
|
session.pop('user', None)
|
|
return redirect(url_for('index'))
|
|
|
|
@app.route('/')
|
|
def index():
|
|
user = session.get('user') # Benutzerinfos aus der Session holen, falls vorhanden
|
|
return render_template('index.html', user=user)
|
|
|
|
@app.route("/reset_quiz/<playlist_id>")
|
|
def reset_quiz(playlist_id):
|
|
session.pop(f'played_tracks_{playlist_id}', None)
|
|
session.pop(f'score_{playlist_id}', None)
|
|
next_mode = request.args.get('next_mode')
|
|
if next_mode:
|
|
return redirect(url_for('quiz', playlist_id=playlist_id, mode=next_mode))
|
|
return redirect(url_for('playlists'))
|
|
|
|
if __name__ == "__main__":
|
|
app.run(host="0.0.0.0", port=5000, debug=True)
|