This commit is contained in:
matthias@matsewe.de
2024-05-21 12:20:25 +02:00
parent df764bd85d
commit 9401e0727b
15 changed files with 425 additions and 84 deletions

View File

@@ -5,6 +5,7 @@ from fastapi.responses import HTMLResponse
from fastapi.templating import Jinja2Templates from fastapi.templating import Jinja2Templates
from app.dependencies import engine from app.dependencies import engine
from app.sql_models import Base from app.sql_models import Base
from app.routers.songs import get_songs
Base.metadata.create_all(engine) Base.metadata.create_all(engine)
@@ -25,6 +26,24 @@ async def root(request: Request, session_id : str = ""):
request=request, name="landing.html" request=request, name="landing.html"
) )
else: else:
songs = await get_songs(session_id)
songs_by_category = {}
all_categories = set()
for song in songs:
if song.main_category not in songs_by_category:
songs_by_category[song.main_category] = []
songs_by_category[song.main_category].append(song)
all_categories.update(song.categories.keys())
return templates.TemplateResponse( return templates.TemplateResponse(
request=request, name="voting.html" request=request, name="voting.html", context={
"songs_by_category": songs_by_category,
"all_categories": {c: i+1 for i, c in enumerate(all_categories)},
"session_id": session_id
}
) )
#@app.get("/vote", response_class=HTMLResponse)
#async def vote(request: Request, session_id : str = ""):
# return templates.TemplateResponse(
# request=request, name="voting-old.html"
# )

View File

@@ -13,8 +13,10 @@ class Song(BaseModel):
id: int id: int
og_artist: Optional[str] og_artist: Optional[str]
aca_artist: Optional[str] aca_artist: Optional[str]
title: str title: Optional[str]
yt_url: Optional[str] url: Optional[str]
yt_id: Optional[str]
spfy_id: Optional[str]
thumbnail: Optional[str] thumbnail: Optional[str]
is_aca: bool is_aca: bool
arng_url: Optional[str] arng_url: Optional[str]

View File

@@ -1,11 +1,17 @@
from fastapi import APIRouter, Security from fastapi import APIRouter, Security
from app.sql_models import SqlSong from app.sql_models import SqlSong
from app.dependencies import session from app.dependencies import session
from app.dependencies import engine
from app.routers.user import get_current_user from app.routers.user import get_current_user
import pandas as pd import pandas as pd
import numpy as np import numpy as np
import re import re
import requests import requests
import os
from app.sql_models import Base
router = APIRouter( router = APIRouter(
prefix="/admin", prefix="/admin",
@@ -22,7 +28,10 @@ def get_main_category(categories) -> int:
else: else:
return np.argmax(categories != None, axis=0) return np.argmax(categories != None, axis=0)
def youtube_url_validation(url): def get_youtube_id(url):
if url is None:
return None
youtube_regex = ( youtube_regex = (
r'(https?://)?(www\.)?' r'(https?://)?(www\.)?'
'(youtube|youtu|youtube-nocookie)\.(com|be)/' '(youtube|youtu|youtube-nocookie)\.(com|be)/'
@@ -30,23 +39,38 @@ def youtube_url_validation(url):
youtube_regex_match = re.match(youtube_regex, url) youtube_regex_match = re.match(youtube_regex, url)
if youtube_regex_match: if youtube_regex_match:
return youtube_regex_match return youtube_regex_match.group(6)
return False return None
def get_thumbnail(url): def get_thumbnail(url):
m = youtube_url_validation(url) if url is None:
return "/static/cover.jpg"
m = get_youtube_id(url)
if m: if m:
thumbnail_url = "https://img.youtube.com/vi/" + m.group(6) + "/mqdefault.jpg" thumbnail_url = "https://img.youtube.com/vi/" + m + "/mqdefault.jpg"
return thumbnail_url return thumbnail_url
elif "spotify" in url: elif "spotify" in url:
return re.findall(r'(https?://i.scdn.co/image[^"]+)', requests.get(url).text)[0] return re.findall(r'(https?://i.scdn.co/image[^"]+)', requests.get(url).text)[0]
else: else:
return "/static/cover.jpg" return "/static/cover.jpg"
@router.post("/process_file") def get_spotify_id(url):
async def create_upload_file(link_share: str): if url is None:
song_list = pd.read_excel(link_share) return None
if "spotify" in url:
return url.split("/track/")[1]
else:
return None
@router.post("/load_list")
async def create_upload_file():
Base.metadata.drop_all(engine)
Base.metadata.create_all(engine)
song_list = pd.read_excel(os.environ['LIST_URL'])
song_list = song_list.replace({np.nan: None}) song_list = song_list.replace({np.nan: None})
song_list = song_list.replace({"n/a": None}) song_list = song_list.replace({"n/a": None})
@@ -55,15 +79,25 @@ async def create_upload_file(link_share: str):
for row in song_list[1:].iterrows(): for row in song_list[1:].iterrows():
row = np.array(row[1]) row = np.array(row[1])
yt_id = get_youtube_id(row[3])
spfy_id = get_spotify_id(row[3])
categories = {n: v for n, v in zip(
category_names, row[6:19] != None)}
if not np.any(list(categories.values())):
continue
s = SqlSong(og_artist=row[0], s = SqlSong(og_artist=row[0],
aca_artist=row[1], aca_artist=row[1],
title=row[2], title=row[2],
yt_url=row[3], url=row[3],
yt_id=yt_id,
spfy_id=spfy_id,
thumbnail=get_thumbnail(row[3]), thumbnail=get_thumbnail(row[3]),
is_aca=row[4] == "ja", is_aca=row[4] == "ja",
arng_url=row[5], arng_url=row[5],
categories={n: v for n, v in zip( categories=categories,
category_names, row[6:19] != None)},
main_category=category_names[get_main_category(row[6:19])], main_category=category_names[get_main_category(row[6:19])],
singable=row[19] != "nein" singable=row[19] != "nein"
) )

View File

@@ -9,13 +9,24 @@ router = APIRouter(
responses={404: {"description": "Not found"}}, responses={404: {"description": "Not found"}},
) )
async def songify(s, votes):
return Song(**s.__dict__, vote=votes.get(s.id, None))
@router.get("/") @router.get("/")
async def get_songs(user_id : str = "") -> list[Song]: async def get_songs(user_id : str = "") -> list[Song]:
sqlsongs = session.query(SqlSong).filter(SqlSong.singable == True).all() sqlsongs = session.query(SqlSong).filter(SqlSong.singable == True).all()
votes = session.query(SqlVote).filter(SqlVote.user_id == user_id).all() votes = session.query(SqlVote).filter(SqlVote.user_id == user_id).all()
votes = {v.song_id : v.vote for v in votes} votes = {v.song_id : v.vote for v in votes}
return [Song(**s.__dict__, vote=votes.get(s.id, None)) for s in sqlsongs] # type: ignore songs = []
for s in sqlsongs:
try:
songs.append(Song(**s.__dict__, vote=votes.get(s.id, None)))
except:
print(s.__dict__)
pass
return songs
#return [Song(**s.__dict__, vote=votes.get(s.id, None)) for s in sqlsongs] # type: ignore
@router.post("/{song_id}/vote") @router.post("/{song_id}/vote")
async def vote(song_id : str, user_id : str, vote : int): async def vote(song_id : str, user_id : str, vote : int):

View File

@@ -6,13 +6,26 @@ from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm, Se
from jose import JWTError, jwt from jose import JWTError, jwt
from passlib.context import CryptContext from passlib.context import CryptContext
from pydantic import BaseModel, ValidationError from pydantic import BaseModel, ValidationError
import os
from app.secrets import SECRET_KEY, fake_users_db #from app.secrets import SECRET_KEY, fake_users_db
# to get a string like this run: # to get a string like this run:
# openssl rand -hex 32 # openssl rand -hex 32
ALGORITHM = "HS256" ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 60 * 24 * 31 ACCESS_TOKEN_EXPIRE_MINUTES = 60 * 24 * 31
SECRET_KEY = os.environ['SECRET_KEY']
fake_users_db = {
"admin": {
"username": "admin",
"email": "admin@example.com",
"hashed_password": os.environ["ADMIN_PWD"],
"disabled": False,
"scopes" : ["admin", "public"]
}
}
class Token(BaseModel): class Token(BaseModel):
@@ -28,7 +41,7 @@ class TokenData(BaseModel):
class User(BaseModel): class User(BaseModel):
username: str username: str
email: str | None = None email: str | None = None
full_name: str | None = None #full_name: str | None = None
disabled: bool | None = None disabled: bool | None = None
@@ -53,6 +66,7 @@ router = APIRouter(
def verify_password(plain_password, hashed_password): def verify_password(plain_password, hashed_password):
print(get_password_hash(plain_password))
return pwd_context.verify(plain_password, hashed_password) return pwd_context.verify(plain_password, hashed_password)

View File

@@ -9,7 +9,9 @@ class SqlSong(Base):
og_artist = Column(String) og_artist = Column(String)
aca_artist = Column(String) aca_artist = Column(String)
title = Column(String) title = Column(String)
yt_url = Column(String) url = Column(String)
yt_id = Column(String)
spfy_id = Column(String)
thumbnail = Column(String) thumbnail = Column(String)
is_aca = Column(Boolean) is_aca = Column(Boolean)
arng_url = Column(String) arng_url = Column(String)

View File

@@ -1,5 +1,4 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg <svg
width="326.09866" width="326.09866"

Before

Width:  |  Height:  |  Size: 789 B

After

Width:  |  Height:  |  Size: 708 B

View File

@@ -1,5 +1,4 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg <svg
width="466.66666" width="466.66666"

Before

Width:  |  Height:  |  Size: 529 B

After

Width:  |  Height:  |  Size: 448 B

21
static/open.svg Normal file
View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="600.07782"
height="600.07782"
viewBox="0 0 18.002335 18.002335"
fill="none"
version="1.1"
id="svg1"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs1" />
<path
d="m 17.002335,1 -8,8 m 8,-8 v 4.5 m 0,-4.5 h -4.5 m 3.5,8.5 v 4.3 c 0,1.1201 0,1.6802 -0.218,2.108 -0.1917,0.3763 -0.4977,0.6823 -0.874,0.874 -0.4278,0.218 -0.9879,0.218 -2.108,0.218 h -8.6 c -1.1201,0 -1.68016,0 -2.10798,-0.218 C 1.718025,16.5903 1.412065,16.2843 1.220325,15.908 1.002335,15.4802 1.002335,14.9201 1.002335,13.8 V 5.2 c 0,-1.1201 0,-1.68016 0.21799,-2.10798 0.19174,-0.37633 0.4977,-0.68229 0.87403,-0.87403 C 2.522175,2 3.082225,2 4.202335,2 h 4.3"
stroke="#000000"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
id="path1" />
</svg>

After

Width:  |  Height:  |  Size: 902 B

View File

@@ -1,5 +1,4 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg <svg
width="259.76822" width="259.76822"

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 971 B

View File

@@ -2,17 +2,31 @@
box-sizing: border-box; box-sizing: border-box;
margin: 0; margin: 0;
padding: 0; padding: 0;
font-family: sans-serif;
}
.vote-buttons,
.cover-container,
.categories {
-webkit-user-select: none; -webkit-user-select: none;
-ms-user-select: none; -ms-user-select: none;
user-select: none; user-select: none;
} }
@media only screen and (min-resolution: 200dpi) { @media only screen and (min-resolution: 200dpi) {
body { body {
font-size: 3.2vmin; font-size: 3.2vmin;
} }
} }
.text {
padding: 0.3em;
margin-bottom: 0.5em;
display: inline-block;
}
.clear { .clear {
clear: both; clear: both;
} }
@@ -101,11 +115,12 @@
.button { .button {
height: 1.5em; height: 1.5em;
width: 3em; width: 3em;
display: inline-block;
text-align: center; text-align: center;
vertical-align: middle; vertical-align: middle;
font-size: 1.5em; font-size: 1.5em;
position: relative; position: relative;
float: left;
clear: none;
} }
.button img { .button img {
@@ -202,6 +217,8 @@
font-size: 0.7em; font-size: 0.7em;
} }
h1 { h1 {
font-family: sans-serif; font-family: sans-serif;
padding: 0.1em; padding: 0.1em;
@@ -224,20 +241,21 @@ h1 {
} }
#spotify-player { #spotify-player {
bottom: 0.2em; bottom: 0.4em;
background-color: blue;
} }
#close-player { #close-player {
position: absolute; position: fixed;
bottom: 320px; bottom: calc(360px + 0.2em - 3em);
right: 0.5em; right: 0.7em;
background-color: rgba(255,255,255,0.9); background-color: rgba(255, 255, 255, 1);
width: 3em; width: 3em;
height: 3em; height: 3em;
border-radius: 1.5em; border-radius: 1.5em;
display: none; display: none;
cursor: pointer;
} }
#close-player img { #close-player img {
position: relative; position: relative;
left: 50%; left: 50%;

54
static/stop.svg Normal file
View File

@@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="176.76801"
height="419.52487"
viewBox="0 0 5.3030402 12.585746"
fill="none"
version="1.1"
id="svg1"
sodipodi:docname="stop.svg"
inkscape:version="1.3.2 (091e20e, 2023-11-25, custom)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="1.916407"
inkscape:cx="104.36196"
inkscape:cy="177.15443"
inkscape:window-width="2560"
inkscape:window-height="1351"
inkscape:window-x="-9"
inkscape:window-y="-9"
inkscape:window-maximized="1"
inkscape:current-layer="svg1" />
<defs
id="defs1" />
<path
d="M 1.0001448,2.2242414 V 10.361541"
stroke="#000000"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
id="path1"
style="stroke:#000000;stroke-opacity:1"
sodipodi:nodetypes="cc" />
<path
d="m 4.2993174,2.22424 v 8.1373"
stroke="#000000"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
id="path1-3"
style="stroke:#000000;stroke-opacity:1"
sodipodi:nodetypes="cc" />
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -143,6 +143,7 @@ $(document).ready(function () {
"no_selected": (song.vote == -1) ? "selected" : "", "no_selected": (song.vote == -1) ? "selected" : "",
"neutral_selected": (song.vote == 0) ? "selected" : "", "neutral_selected": (song.vote == 0) ? "selected" : "",
"yes_selected": (song.vote == 1) ? "selected" : "", "yes_selected": (song.vote == 1) ? "selected" : "",
"play_button": (song.yt_id || song.spfy_id) ? "play" : "open",
"categories": cats "categories": cats
})).join('') })).join('')
@@ -175,22 +176,30 @@ window.onSpotifyIframeApiReady = (IFrameAPI) => {
IFrameAPI.createController(element, options, callback); IFrameAPI.createController(element, options, callback);
}; };
function stop() {
$("#song-" + is_playing + " .cover-container .overlay img").attr("src", "/static/play.svg");
function play(id) {
$("#yt-player").css("display", "none"); $("#yt-player").css("display", "none");
$("#spotify-player").css("display", "none"); $("#spotify-player").css("display", "none");
$("#close-player").css("display", "none"); $("#close-player").css("display", "none");
$("#yt-player").html(""); $("#yt-player").html("");
spotify_embed_controller.pause(); spotify_embed_controller.pause();
if (is_playing == id) {
is_playing = -1; is_playing = -1;
}
function play(id) {
if (is_playing == id) {
stop();
} else { } else {
stop();
is_playing = id; is_playing = id;
$("#song-" + id + " .cover-container .overlay img").attr("src", "/static/stop.svg");
song = all_songs[id]; song = all_songs[id];
yt_id = song.yt_url.split('v=')[1] yt_id = song.yt_id
spotify_id = song.yt_url.split('/track/')[1] spotify_id = song.spfy_id
if (yt_id) { if (yt_id) {
$("#yt-player").css("display", "flex"); $("#yt-player").css("display", "flex");
@@ -205,15 +214,9 @@ function play(id) {
spotify_embed_controller.play(); spotify_embed_controller.play();
} }
else { else {
$("#yt-player").css("display", "none"); stop();
$("#spotify-player").css("display", "none"); $("#song-" + id + " .cover-container .overlay img").attr("src", "/static/open.svg");
$("#yt-player").html(""); window.open(song.url, '_blank').focus();
spotify_embed_controller.pause();
window.open(song.yt_url, '_blank').focus();
} }
} }
} }
//<iframe style="border-radius:12px" src="https://open.spotify.com/embed/track/2DS7lDZNFM7safSGNm8vd4?utm_source=generator" width="100%" height="352" frameBorder="0" allowfullscreen="" allow="autoplay; clipboard-write; encrypted-media; fullscreen; picture-in-picture" loading="lazy"></iframe>
// https://open.spotify.com/intl-de/track/2DS7lDZNFM7safSGNm8vd4

54
templates/voting-old.html Normal file
View File

@@ -0,0 +1,54 @@
<!DOCTYPE html>
<html>
<head>
<title>Liederwahl</title>
<link rel="stylesheet" type="text/css" href="/static/colors.css">
<link rel="stylesheet" type="text/css" href="/static/site.css">
<script src="https://open.spotify.com/embed/iframe-api/v1" async></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<script src="/static/voting_script.js"></script>
<script type="text/template" data-template="song">
<div class="song" id="song-${id}">
<div class="cover-container">
<img src="${cover_image}" class="cover">
<div class="overlay" onclick="play(${id})"><img src="/static/${play_button}.svg"></div>
</div>
<div class="song-title">${title}</div>
<div class="song-artist">${artist}</div>
<div class="categories" id="container">
${categories}<span style="--main-color: transparent;">&nbsp;</span>
</div>
<div class="vote-buttons">
<div class="button button-no ${no_selected}" onmousedown="vote(${id}, -1); return false;" onclick="return false;"><img src="/static/no.svg"></div><div class="button button-neutral ${neutral_selected}" onmousedown="vote(${id}, 0)"><img src="/static/neutral.svg"></div><div class="button button-yes ${yes_selected}" onmousedown="vote(${id}, 1)"><img src="/static/yes.svg"></div>
</div>
<div class="clear"></div>
</div>
</script>
</head>
<body>
<h1 style="--main-color: #888888; ">Hallo :)</h1>
<div class="text">Du kannst die Liederwahl jederzeit unterbrechen und zu einem späteren Zeitpunkt weitermachen.</div>
<div id="songs"></div>
<div id="spotify-player"><div id="spotify-embed"></div></div>
<div id="yt-player"></div>
<div id="close-player" onclick="stop(); return false;"><img src="/static/no.svg"></div>
<!--<div class="song">
<div class="cover-container">
<img src="{{ url_for('static', path='/cover.jpg') }}" class="cover">
<div class="overlay"><img src="{{ url_for('static', path='/play.svg') }}"></div>
</div>
<div class="song-data">VoicePlay: In The Air Tonight</div>
<div class="categories" id="container">
<span class="cat-1">Ballade</span><span class="cat-2">&lt; 90er Remake</span><span class="cat-3">Something else </span><span class="cat-4">Something else </span>
</div>
<div class="vote-buttons">
<div class="button button-no"><img src="{{ url_for('static', path='/no.svg') }}"></div><div class="button button-neutral"><img src="{{ url_for('static', path='/neutral.svg') }}"></div><div class="button button-yes selected"><img src="{{ url_for('static', path='/yes.svg') }}"></div>
</div>
<div class="clear"></div>
</div>-->
</body>
</html>

View File

@@ -1,52 +1,164 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<title>Liederwahl</title> <title>Liederwahl</title>
<link rel="apple-touch-icon" href="https://choriosity.de/assets/images/apple-touch-icon.png" type="image/png">
<link rel="alternate icon" href="https://choriosity.de/assets/images/favicon.png" type="image/png">
<link rel="shortcut icon" href="https://choriosity.de/assets/images/favicon.svg" type="image/svg+xml">
<link rel="stylesheet" type="text/css" href="/static/colors.css"> <link rel="stylesheet" type="text/css" href="/static/colors.css">
<link rel="stylesheet" type="text/css" href="/static/site.css"> <link rel="stylesheet" type="text/css" href="/static/site.css">
<script src="https://open.spotify.com/embed/iframe-api/v1" async></script> <script src="https://open.spotify.com/embed/iframe-api/v1" async></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script> <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<script src="/static/voting_script.js"></script> <script type="text/javascript">
<script type="text/template" data-template="song"> var spotify_embed_controller;
<div class="song" id="song-${id}">
<div class="cover-container">
<img src="${cover_image}" class="cover">
<div class="overlay" onclick="play(${id})"><img src="/static/play.svg"></div>
</div>
<div class="song-title">${title}</div>
<div class="song-artist">${artist}</div>
<div class="categories" id="container">
${categories}<span style="--main-color: transparent;">&nbsp;</span>
</div>
<div class="vote-buttons">
<div class="button button-no ${no_selected}" onmousedown="vote(${id}, -1); return false;" onclick="return false;"><img src="/static/no.svg"></div><div class="button button-neutral ${neutral_selected}" onmousedown="vote(${id}, 0)"><img src="/static/neutral.svg"></div><div class="button button-yes ${yes_selected}" onmousedown="vote(${id}, 1)"><img src="/static/yes.svg"></div>
</div>
<div class="clear"></div>
</div>
</script>
window.onSpotifyIframeApiReady = (IFrameAPI) => {
const element = document.getElementById('spotify-embed');
const options = {
width: '640',
height: '360'
};
const callback = (EmbedController) => {
spotify_embed_controller = EmbedController;
};
IFrameAPI.createController(element, options, callback);
};
var is_playing = -1;
function stop() {
$("#song-" + is_playing + " .cover-container .overlay img").attr("src", "/static/play.svg");
$("#yt-player").css("display", "none");
$("#spotify-player").css("display", "none");
$("#close-player").css("display", "none");
$("#yt-player").html("");
spotify_embed_controller.pause();
is_playing = -1;
}
function playYt(song_id, yt_id) {
if (is_playing == song_id) {
stop();
} else {
stop();
is_playing = song_id;
$("#song-" + song_id + " .cover-container .overlay img").attr("src", "/static/stop.svg");
$("#yt-player").css("display", "flex");
$("#close-player").css("display", "block");
iframe_code = '<iframe src="https://www.youtube.com/embed/' + yt_id + '?autoplay=1" title="" width="640" height="360" frameBorder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowFullScreen></iframe>';
$("#yt-player").html(iframe_code);
}
}
function playSpfy(song_id, spfy_id) {
if (is_playing == song_id) {
stop();
} else {
stop();
is_playing = song_id;
$("#song-" + song_id + " .cover-container .overlay img").attr("src", "/static/stop.svg");
$("#spotify-player").css("display", "flex");
$("#close-player").css("display", "block");
spotify_embed_controller.loadUri("spotify:track:" + spfy_id);
spotify_embed_controller.play();
}
}
function openUrl(song_id, url) {
stop();
window.open(url, '_blank').focus();
}
function vote(song_id, vote) {
var session_id = "{{ session_id }}";
no_button = $("#song-" + song_id).find(".button-no")
yes_button = $("#song-" + song_id).find(".button-yes")
neutral_button = $("#song-" + song_id).find(".button-neutral")
no_button.removeClass("selected")
yes_button.removeClass("selected")
neutral_button.removeClass("selected")
switch (vote) {
case 0:
neutral_button.addClass("selected")
break;
case 1:
yes_button.addClass("selected")
break;
case -1:
no_button.addClass("selected")
default:
break;
}
$.ajax({
url: "/songs/" + song_id + "/vote?" + $.param({ user_id: session_id, vote: vote }),
method: "POST"
})
}
</script>
</head> </head>
<body> <body>
<div id="songs"></div> <h1 style="--main-color: #888888; ">Hallo :)</h1>
<div id="spotify-player"><div id="spotify-embed"></div></div> <div class="text">Du kannst die Liederwahl jederzeit unterbrechen und zu einem späteren Zeitpunkt weitermachen.
<div id="yt-player"></div>
<div id="close-player"><img src="/static/no.svg"></div>
<!--<div class="song">
<div class="cover-container">
<img src="{{ url_for('static', path='/cover.jpg') }}" class="cover">
<div class="overlay"><img src="{{ url_for('static', path='/play.svg') }}"></div>
</div> </div>
<div class="song-data">VoicePlay: In The Air Tonight</div> <div id="songs">
<div class="categories" id="container"> {% for main_category, songs in songs_by_category.items() %}
<span class="cat-1">Ballade</span><span class="cat-2">&lt; 90er Remake</span><span class="cat-3">Something else </span><span class="cat-4">Something else </span> <h1 class="cat-{{ all_categories[main_category] }}">{{ main_category }}</h1>
{% for song in songs -%}
<div class="song" id="song-{{ song.id }}">
<div class="cover-container">
<img src="{{ song.thumbnail }}" class="cover">
<div class="overlay"
onclick="{% if song.yt_id %}playYt({{ song.id }}, '{{ song.yt_id }}'){% else %}{% if song.spfy_id %}playSpfy({{ song.id }}, '{{ song.spfy_id }}'){% else %}openUrl({{ song.id }}, '{{ song.url }}'){% endif %}{% endif %}">
<img src="/static/{% if song.yt_id or song.spfy_id %}play{% else %}open{% endif %}.svg">
</div>
</div>
<div class="song-title">{{ song.title }}</div>
<div class="song-artist">{% if song.og_artist %}{{ song.og_artist }}{% if song.aca_artist and
song.aca_artist != song.og_artist %} / {{ song.aca_artist
}}{% endif %}{% else %}{{ song.aca_artist }}{% endif %}</div>
<div class="categories" id="container">{% for category_name, is_in_category in song.categories.items() %}{%
if is_in_category %}<span class="cat-{{ all_categories[category_name] }}">{{ category_name }}</span>{%
endif %}{% endfor %}<span style="--main-color: transparent;">&nbsp;</span>
</div> </div>
<div class="vote-buttons"> <div class="vote-buttons">
<div class="button button-no"><img src="{{ url_for('static', path='/no.svg') }}"></div><div class="button button-neutral"><img src="{{ url_for('static', path='/neutral.svg') }}"></div><div class="button button-yes selected"><img src="{{ url_for('static', path='/yes.svg') }}"></div> <div class="button button-no {% if song.vote == -1 %}selected{% endif %}"
onmousedown="vote({{ song.id }}, -1); return false;" onclick="return false;"><img
src="/static/no.svg">
</div>
<div class="button button-neutral {% if song.vote == 0 %}selected{% endif %}"
onmousedown="vote({{ song.id }}, 0); return false;" onclick="return false;"><img
src="/static/neutral.svg"></div>
<div class="button button-yes {% if song.vote == 1 %}selected{% endif %}"
onmousedown="vote({{ song.id }}, 1); return false;" onclick="return false;"><img
src="/static/yes.svg">
</div>
</div> </div>
<div class="clear"></div> <div class="clear"></div>
</div>--> </div>
</body> {% endfor %}
</html> {% endfor %}
</div>
<div id="spotify-player">
<div id="spotify-embed"></div>
</div>
<div id="yt-player"></div>
<div id="close-player" onclick="stop(); return false;"><img src="/static/no.svg"></div>
</body>
</html>