Erste version
This commit is contained in:
commit
6e7fb8c499
5 changed files with 222 additions and 0 deletions
129
app.py
Executable file
129
app.py
Executable file
|
|
@ -0,0 +1,129 @@
|
||||||
|
from flask import Flask, render_template, request, redirect
|
||||||
|
from glob import glob
|
||||||
|
import subprocess
|
||||||
|
import requests
|
||||||
|
import json
|
||||||
|
|
||||||
|
def wikiapi(id,field):
|
||||||
|
name = f"data/{id}_{field}.json"
|
||||||
|
try:
|
||||||
|
with open(name, "r") as f:
|
||||||
|
data = json.load(f)
|
||||||
|
except OSError:
|
||||||
|
r = requests.get("https://www.wikidata.org/w/rest.php/wikibase/v1/entities/items/"+id+"?_fields="+field, headers={"user-agent":'esc/0.0(mikka@kleinrot.net)','Authorization': 'Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJhdWQiOiJhNDBhYmZjYzQxZTlkOTc2YzJiN2NmODZkYzIwMmQzZiIsImp0aSI6ImQ3ODQ4YmJhNjZlMWYzYzdhZGZmODU5NTdmZWE1YjMxODBhMzE2NTc4YWYyMWMzZGZhZGJiOTZlZmM3ZTgyMmJkMTE4YTQ4YWMzMDM2ZmQyIiwiaWF0IjoxNzc1OTE4OTc1LjkzMjQsIm5iZiI6MTc3NTkxODk3NS45MzI0MDEsImV4cCI6MzMzMzI4Mjc3NzUuOTI5NDA1LCJzdWIiOiIxMzEiLCJpc3MiOiJodHRwczovL21ldGEud2lraW1lZGlhLm9yZyIsInJhdGVsaW1pdCI6eyJyZXF1ZXN0c19wZXJfdW5pdCI6NTAwMCwidW5pdCI6IkhPVVIifSwic2NvcGVzIjpbImJhc2ljIl19.WLXRXxNjsZ1kIK2RX-Wds59W8Z1CWphbiV3UBEWvm5STWZ6uvpHZpx5d9lomK5-H56G3259_DavuTymdebQTmAwkeuCGh1gb60u9_4MzmGgspQHBPq3mWX879AUVW69uqsSz2x2zIz-ZUmt5TrZM7X3OBxE01nhVBoiDWnlW4Gt19FjwBQ9WjXzrFHsH9A7BtPzgbKROMXACRQBqh9XZXkN23PvnEV08sx63AGB0cZBkteIsnIfm49-Ht2_DTZKd4_MpEw9TZjOHSjY9odyImXidZOgy7NadT-LwAKQiVlSn6UZWTvgsG6xwP6sad_ctqU1ZupcOJLzd158DhrLAEMk_Qq9-piTQDYtLzWfLx44Jatjae6-0nilOgvrB0XQJC6cTihVk3-9Lyn6Y-e6yIDnEHIIIBj0J8PUv5IJjAojo-2uZ4w8omEzCWDEzf95_GSZmRwj_nRNCVs5jQpVhG9hFpOm2OrTrhqPgYet_dsl3QVEbvtGnlVzCw7j9sj_86axAC1hklQQso6AiAAzEmOiTfC6VyrP8sFjbnDlLBCEiTkYTvOTp5nuYRC8VdwJQm6NJIafjNn5ihp4T8OvJQyzsK1HDYlthvW54-co2gPoeIX-cBItvdrCBNDofoA9bIP3zzwGXdvy1b7lofx5WekWKAGytPCfsr7i2ZJB93xU'})
|
||||||
|
data = r.json()
|
||||||
|
with open(name, "x") as f:
|
||||||
|
json.dump(data, f,indent=2,ensure_ascii=False)
|
||||||
|
return data[field]
|
||||||
|
|
||||||
|
def wikisuche(text):
|
||||||
|
mitp1344 = []
|
||||||
|
ohnep1344 = []
|
||||||
|
r = requests.get("https://www.wikidata.org/w/rest.php/wikibase/v1/search/items?q="+text+"&language=en", headers={"user-agent":'esc/0.0(mikka@kleinrot.net)','Authorization': 'Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJhdWQiOiJhNDBhYmZjYzQxZTlkOTc2YzJiN2NmODZkYzIwMmQzZiIsImp0aSI6ImQ3ODQ4YmJhNjZlMWYzYzdhZGZmODU5NTdmZWE1YjMxODBhMzE2NTc4YWYyMWMzZGZhZGJiOTZlZmM3ZTgyMmJkMTE4YTQ4YWMzMDM2ZmQyIiwiaWF0IjoxNzc1OTE4OTc1LjkzMjQsIm5iZiI6MTc3NTkxODk3NS45MzI0MDEsImV4cCI6MzMzMzI4Mjc3NzUuOTI5NDA1LCJzdWIiOiIxMzEiLCJpc3MiOiJodHRwczovL21ldGEud2lraW1lZGlhLm9yZyIsInJhdGVsaW1pdCI6eyJyZXF1ZXN0c19wZXJfdW5pdCI6NTAwMCwidW5pdCI6IkhPVVIifSwic2NvcGVzIjpbImJhc2ljIl19.WLXRXxNjsZ1kIK2RX-Wds59W8Z1CWphbiV3UBEWvm5STWZ6uvpHZpx5d9lomK5-H56G3259_DavuTymdebQTmAwkeuCGh1gb60u9_4MzmGgspQHBPq3mWX879AUVW69uqsSz2x2zIz-ZUmt5TrZM7X3OBxE01nhVBoiDWnlW4Gt19FjwBQ9WjXzrFHsH9A7BtPzgbKROMXACRQBqh9XZXkN23PvnEV08sx63AGB0cZBkteIsnIfm49-Ht2_DTZKd4_MpEw9TZjOHSjY9odyImXidZOgy7NadT-LwAKQiVlSn6UZWTvgsG6xwP6sad_ctqU1ZupcOJLzd158DhrLAEMk_Qq9-piTQDYtLzWfLx44Jatjae6-0nilOgvrB0XQJC6cTihVk3-9Lyn6Y-e6yIDnEHIIIBj0J8PUv5IJjAojo-2uZ4w8omEzCWDEzf95_GSZmRwj_nRNCVs5jQpVhG9hFpOm2OrTrhqPgYet_dsl3QVEbvtGnlVzCw7j9sj_86axAC1hklQQso6AiAAzEmOiTfC6VyrP8sFjbnDlLBCEiTkYTvOTp5nuYRC8VdwJQm6NJIafjNn5ihp4T8OvJQyzsK1HDYlthvW54-co2gPoeIX-cBItvdrCBNDofoA9bIP3zzwGXdvy1b7lofx5WekWKAGytPCfsr7i2ZJB93xU'})
|
||||||
|
for result in r.json()["results"]:
|
||||||
|
if getescedition(result["id"]):
|
||||||
|
mitp1344.append(result)
|
||||||
|
else:
|
||||||
|
ohnep1344.append(result)
|
||||||
|
return mitp1344 + ohnep1344
|
||||||
|
|
||||||
|
def getescedition(songId):
|
||||||
|
statements = wikiapi(songId,"statements")
|
||||||
|
if "P1344" in statements:
|
||||||
|
for statement in statements["P1344"]:
|
||||||
|
id = statement["value"]["content"]
|
||||||
|
escdata = wikiapi(id,"statements")
|
||||||
|
if escdata["P31"][0]["value"]["content"] == "Q110288240":
|
||||||
|
return id,statement
|
||||||
|
|
||||||
|
|
||||||
|
def datei(id):
|
||||||
|
file = glob("static/"+id+".*")
|
||||||
|
|
||||||
|
if file == []:
|
||||||
|
subprocess.run(["yt-dlp",f"http://youtu.be/{id}","-x","-o",id],cwd="static")
|
||||||
|
file = glob("static/"+id+".*")
|
||||||
|
audio = file[0].replace("static/", "")
|
||||||
|
|
||||||
|
return audio
|
||||||
|
|
||||||
|
with open("songs.json","r") as f:
|
||||||
|
songs = json.load(f)
|
||||||
|
def kartenGeneriren(song):
|
||||||
|
id = song["wikiid"]
|
||||||
|
statements = wikiapi(id,"statements")
|
||||||
|
labels = wikiapi(id,"labels")
|
||||||
|
song["titel"] = statements.get("P1476",[{"value": {"content":{"text":labels["en"]}}}])[0]["value"]["content"]["text"]
|
||||||
|
song["laenge"] = int(statements.get("P2047",[{"value": {"content":{"amount":"0"}}}])[0]["value"]["content"]["amount"])
|
||||||
|
escedition = getescedition(id)
|
||||||
|
if escedition:
|
||||||
|
(escedition,statement)=escedition
|
||||||
|
song["jahrgang"] = wikiapi(escedition,"labels")["en"]
|
||||||
|
qualifiers = statement["qualifiers"]
|
||||||
|
for qualifier in qualifiers:
|
||||||
|
if qualifier["property"]["id"] == "P1352":
|
||||||
|
song["plazirung"] = str(int(qualifier["value"]["content"]["amount"]))+"."
|
||||||
|
song["interprete"] = " x ".join(set(wikiapi(statement["value"]["content"],"labels")["en"] for statement in statements.get("P175",[])))
|
||||||
|
if "ytid" not in song and "P1651" in statements:
|
||||||
|
song["ytid"] = statements["P1651"][0]["value"]["content"]
|
||||||
|
print(song)
|
||||||
|
if "ytid" in song:
|
||||||
|
song["datei"] = datei(song["ytid"])
|
||||||
|
song["img"] = f"http://img.youtube.com/vi/{ song["ytid"] }/sddefault.jpg"
|
||||||
|
if "P6218" in statements:
|
||||||
|
song["text"] = f"//genius.com/{statements["P6218"][0]["value"]["content"]}"
|
||||||
|
if "P18" in statements:
|
||||||
|
song["img"] = f"https://commons.wikimedia.org/w/index.php?title=Special:Redirect/file/{statements["P18"][0]["value"]["content"]}&width=300"
|
||||||
|
if "img" not in song:
|
||||||
|
song["img"] = "https://commons.wikimedia.org/w/index.php?title=Special:Redirect/file/Eurovision_Song_Contest_heart_(2014–2025).svg&width=300"
|
||||||
|
for song in songs:
|
||||||
|
kartenGeneriren(song)
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
|
||||||
|
@app.template_filter('zeit')
|
||||||
|
def zeit(sekunden):
|
||||||
|
minuten = sekunden // 60
|
||||||
|
rest = sekunden - minuten * 60
|
||||||
|
return f"{minuten}:{rest:02}"
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/")
|
||||||
|
def hello_world():
|
||||||
|
gesamtLaenge = 0
|
||||||
|
for song in songs:
|
||||||
|
gesamtLaenge += song["laenge"]
|
||||||
|
return render_template('index.html', karten=songs,gesamtLaenge=gesamtLaenge )
|
||||||
|
|
||||||
|
@app.route("/suche")
|
||||||
|
def suche():
|
||||||
|
such = request.args.get("suche")
|
||||||
|
if such is not None:
|
||||||
|
ergebnisse = wikisuche(such)
|
||||||
|
else:
|
||||||
|
ergebnisse = []
|
||||||
|
return render_template('suche.html', ergebnisse=ergebnisse,anfrage=such)
|
||||||
|
|
||||||
|
@app.route("/suche",methods=["POST"])
|
||||||
|
def suche_finden():
|
||||||
|
song = {"wikiid":request.form.get("id")}
|
||||||
|
kartenGeneriren(song)
|
||||||
|
songs.append(song)
|
||||||
|
with open("songs.json","w") as f:
|
||||||
|
json.dump(songs,f,indent=2,ensure_ascii=False)
|
||||||
|
return redirect("/",303)
|
||||||
|
|
||||||
|
@app.route("/ADMIN")
|
||||||
|
def admin():
|
||||||
|
gesamtLaenge = 0
|
||||||
|
for song in songs:
|
||||||
|
gesamtLaenge += song["laenge"]
|
||||||
|
return render_template('index.html', karten=songs,gesamtLaenge=gesamtLaenge,admin=True )
|
||||||
|
|
||||||
|
@app.route("/ADMIN",methods=["POST"])
|
||||||
|
def loeschen():
|
||||||
|
song = int(request.form.get("index"))
|
||||||
|
del songs[song]
|
||||||
|
with open("songs.json","w") as f:
|
||||||
|
json.dump(songs,f,indent=2,ensure_ascii=False)
|
||||||
|
return redirect("/ADMIN",303)
|
||||||
6
static/script.js
Executable file
6
static/script.js
Executable file
|
|
@ -0,0 +1,6 @@
|
||||||
|
const audios = document.querySelectorAll("audio")
|
||||||
|
|
||||||
|
for (const [index,audio] of audios.entries()){
|
||||||
|
audio.addEventListener("ended", (event) => { audios[index + 1].play()})
|
||||||
|
}
|
||||||
|
console.log(audios)
|
||||||
58
templates/index.html
Executable file
58
templates/index.html
Executable file
|
|
@ -0,0 +1,58 @@
|
||||||
|
<style>
|
||||||
|
#flex-container{
|
||||||
|
overflow: auto;
|
||||||
|
scroll-snap-type: x mandatory;
|
||||||
|
display: flex;
|
||||||
|
border: none;
|
||||||
|
gap: 5px;
|
||||||
|
}
|
||||||
|
article{
|
||||||
|
flex-wrap: row nowrap;
|
||||||
|
border: none;
|
||||||
|
flex: 0 0 30rem;
|
||||||
|
}
|
||||||
|
a,h1,h2{
|
||||||
|
display: block;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
article{
|
||||||
|
border: 2px solid;
|
||||||
|
border-radius: 15px;
|
||||||
|
border-color: black;
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 100%;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
#kursive{
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
img{
|
||||||
|
border-radius: 2%;
|
||||||
|
width: 100%;
|
||||||
|
aspect-ratio: 4/3;
|
||||||
|
object-fit: contain;
|
||||||
|
}
|
||||||
|
a{
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
h2{
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
article > a{
|
||||||
|
font-size: 0.75rem;
|
||||||
|
display: block;
|
||||||
|
text-align: center;
|
||||||
|
color: rgb(181, 181, 181);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<h1>ESC Playlist</h1>
|
||||||
|
<h2>2026</h2>
|
||||||
|
<a href="/suche">Du willst ein weiteres Lied in der Playlist?</a>
|
||||||
|
<p>Die Playlist ist insgesamt lang {{ gesamtLaenge | zeit }} und enthält {{ karten|length }} Lieder</p>
|
||||||
|
<div id = "flex-container">
|
||||||
|
{% for karte in karten %}
|
||||||
|
{% set karte_loop = loop %}
|
||||||
|
{% include "karte.html" %}
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
<script src="static/script.js"></script>
|
||||||
17
templates/karte.html
Executable file
17
templates/karte.html
Executable file
|
|
@ -0,0 +1,17 @@
|
||||||
|
<article>
|
||||||
|
<img src="{{ karte.img }}"/>
|
||||||
|
<audio controls autoplay src="/static/{{karte.datei}}"></audio>
|
||||||
|
<h2>{{ karte.titel}}</h2>
|
||||||
|
<p><b>Übersetzter Titel:</b>{{ karte.uetitel}}</p>
|
||||||
|
<p><b>Interpreten:</b>{{ karte.interprete}}</p>
|
||||||
|
<p><b>Jahrgang:</b> {{ karte.jahrgang }}</p>
|
||||||
|
<p><b>Plazirung:</b> {{ karte.plazirung }}</p>
|
||||||
|
<p><b>Länge:</b> {{karte.laenge|zeit}}</p>
|
||||||
|
<p><b id = "kursive">Interesanter</b><b> Fakt:</b> {{ karte.interesanterfakt }}</p>
|
||||||
|
{% if karte.text %}
|
||||||
|
<a href="{{ karte.text }}" target="_blank">Original Text</a>
|
||||||
|
{% endif %}
|
||||||
|
{% if admin %}
|
||||||
|
<form method="post"><button name="index" value="{{karte_loop.index0}}"><X></button></form>
|
||||||
|
{% endif %}
|
||||||
|
</article>
|
||||||
12
templates/suche.html
Executable file
12
templates/suche.html
Executable file
|
|
@ -0,0 +1,12 @@
|
||||||
|
<form><input name="suche" placeholder="Suche" value="{{anfrage or ""}}"></form>
|
||||||
|
{% if ergebnisse %}
|
||||||
|
<form method="post"><ol>
|
||||||
|
{% for ergebnis in ergebnisse %}
|
||||||
|
<li><button name="id" value={{ergebnis.id}}>{{ ergebnis["display-label"].value }}</button> <ul><li>{{ ergebnis.description.value }}</li></ul></li>
|
||||||
|
{% endfor %}</ol></form>
|
||||||
|
|
||||||
|
{%elif anfrage%}
|
||||||
|
<p>Leider gibt es diesen Eintrag nicht!</p>
|
||||||
|
{%endif%}
|
||||||
|
|
||||||
|
<a href="/">Doch nicht?</a>
|
||||||
Loading…
Add table
Add a link
Reference in a new issue