update
21
app/main.py
@@ -5,6 +5,7 @@ from fastapi.responses import HTMLResponse
|
||||
from fastapi.templating import Jinja2Templates
|
||||
from app.dependencies import engine
|
||||
from app.sql_models import Base
|
||||
from app.routers.songs import get_songs
|
||||
|
||||
Base.metadata.create_all(engine)
|
||||
|
||||
@@ -25,6 +26,24 @@ async def root(request: Request, session_id : str = ""):
|
||||
request=request, name="landing.html"
|
||||
)
|
||||
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(
|
||||
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"
|
||||
# )
|
||||
@@ -13,8 +13,10 @@ class Song(BaseModel):
|
||||
id: int
|
||||
og_artist: Optional[str]
|
||||
aca_artist: Optional[str]
|
||||
title: str
|
||||
yt_url: Optional[str]
|
||||
title: Optional[str]
|
||||
url: Optional[str]
|
||||
yt_id: Optional[str]
|
||||
spfy_id: Optional[str]
|
||||
thumbnail: Optional[str]
|
||||
is_aca: bool
|
||||
arng_url: Optional[str]
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
from fastapi import APIRouter, Security
|
||||
from app.sql_models import SqlSong
|
||||
from app.dependencies import session
|
||||
from app.dependencies import engine
|
||||
from app.routers.user import get_current_user
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
import re
|
||||
import requests
|
||||
import os
|
||||
from app.sql_models import Base
|
||||
|
||||
|
||||
|
||||
|
||||
router = APIRouter(
|
||||
prefix="/admin",
|
||||
@@ -22,7 +28,10 @@ def get_main_category(categories) -> int:
|
||||
else:
|
||||
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 = (
|
||||
r'(https?://)?(www\.)?'
|
||||
'(youtube|youtu|youtube-nocookie)\.(com|be)/'
|
||||
@@ -30,23 +39,38 @@ def youtube_url_validation(url):
|
||||
|
||||
youtube_regex_match = re.match(youtube_regex, url)
|
||||
if youtube_regex_match:
|
||||
return youtube_regex_match
|
||||
return youtube_regex_match.group(6)
|
||||
|
||||
return False
|
||||
return None
|
||||
|
||||
def get_thumbnail(url):
|
||||
m = youtube_url_validation(url)
|
||||
if url is None:
|
||||
return "/static/cover.jpg"
|
||||
|
||||
m = get_youtube_id(url)
|
||||
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
|
||||
elif "spotify" in url:
|
||||
return re.findall(r'(https?://i.scdn.co/image[^"]+)', requests.get(url).text)[0]
|
||||
else:
|
||||
return "/static/cover.jpg"
|
||||
|
||||
@router.post("/process_file")
|
||||
async def create_upload_file(link_share: str):
|
||||
song_list = pd.read_excel(link_share)
|
||||
def get_spotify_id(url):
|
||||
if url is None:
|
||||
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({"n/a": None})
|
||||
|
||||
@@ -55,15 +79,25 @@ async def create_upload_file(link_share: str):
|
||||
for row in song_list[1:].iterrows():
|
||||
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],
|
||||
aca_artist=row[1],
|
||||
title=row[2],
|
||||
yt_url=row[3],
|
||||
url=row[3],
|
||||
yt_id=yt_id,
|
||||
spfy_id=spfy_id,
|
||||
thumbnail=get_thumbnail(row[3]),
|
||||
is_aca=row[4] == "ja",
|
||||
arng_url=row[5],
|
||||
categories={n: v for n, v in zip(
|
||||
category_names, row[6:19] != None)},
|
||||
categories=categories,
|
||||
main_category=category_names[get_main_category(row[6:19])],
|
||||
singable=row[19] != "nein"
|
||||
)
|
||||
|
||||
@@ -9,13 +9,24 @@ router = APIRouter(
|
||||
responses={404: {"description": "Not found"}},
|
||||
)
|
||||
|
||||
async def songify(s, votes):
|
||||
return Song(**s.__dict__, vote=votes.get(s.id, None))
|
||||
|
||||
@router.get("/")
|
||||
async def get_songs(user_id : str = "") -> list[Song]:
|
||||
sqlsongs = session.query(SqlSong).filter(SqlSong.singable == True).all()
|
||||
votes = session.query(SqlVote).filter(SqlVote.user_id == user_id).all()
|
||||
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")
|
||||
async def vote(song_id : str, user_id : str, vote : int):
|
||||
|
||||
@@ -6,13 +6,26 @@ from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm, Se
|
||||
from jose import JWTError, jwt
|
||||
from passlib.context import CryptContext
|
||||
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:
|
||||
# openssl rand -hex 32
|
||||
|
||||
ALGORITHM = "HS256"
|
||||
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):
|
||||
@@ -28,7 +41,7 @@ class TokenData(BaseModel):
|
||||
class User(BaseModel):
|
||||
username: str
|
||||
email: str | None = None
|
||||
full_name: str | None = None
|
||||
#full_name: str | None = None
|
||||
disabled: bool | None = None
|
||||
|
||||
|
||||
@@ -53,6 +66,7 @@ router = APIRouter(
|
||||
|
||||
|
||||
def verify_password(plain_password, hashed_password):
|
||||
print(get_password_hash(plain_password))
|
||||
return pwd_context.verify(plain_password, hashed_password)
|
||||
|
||||
|
||||
|
||||
@@ -9,7 +9,9 @@ class SqlSong(Base):
|
||||
og_artist = Column(String)
|
||||
aca_artist = Column(String)
|
||||
title = Column(String)
|
||||
yt_url = Column(String)
|
||||
url = Column(String)
|
||||
yt_id = Column(String)
|
||||
spfy_id = Column(String)
|
||||
thumbnail = Column(String)
|
||||
is_aca = Column(Boolean)
|
||||
arng_url = Column(String)
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
|
||||
<svg
|
||||
width="326.09866"
|
||||
|
||||
|
Before Width: | Height: | Size: 789 B After Width: | Height: | Size: 708 B |
@@ -1,5 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
|
||||
<svg
|
||||
width="466.66666"
|
||||
|
||||
|
Before Width: | Height: | Size: 529 B After Width: | Height: | Size: 448 B |
21
static/open.svg
Normal 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 |
@@ -1,5 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
|
||||
<svg
|
||||
width="259.76822"
|
||||
|
||||
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 971 B |
@@ -2,17 +2,31 @@
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
.vote-buttons,
|
||||
.cover-container,
|
||||
.categories {
|
||||
-webkit-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
|
||||
@media only screen and (min-resolution: 200dpi) {
|
||||
body {
|
||||
font-size: 3.2vmin;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.text {
|
||||
padding: 0.3em;
|
||||
margin-bottom: 0.5em;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.clear {
|
||||
clear: both;
|
||||
}
|
||||
@@ -101,11 +115,12 @@
|
||||
.button {
|
||||
height: 1.5em;
|
||||
width: 3em;
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
font-size: 1.5em;
|
||||
position: relative;
|
||||
float: left;
|
||||
clear: none;
|
||||
}
|
||||
|
||||
.button img {
|
||||
@@ -202,6 +217,8 @@
|
||||
font-size: 0.7em;
|
||||
}
|
||||
|
||||
|
||||
|
||||
h1 {
|
||||
font-family: sans-serif;
|
||||
padding: 0.1em;
|
||||
@@ -224,20 +241,21 @@ h1 {
|
||||
}
|
||||
|
||||
#spotify-player {
|
||||
bottom: 0.2em;
|
||||
background-color: blue;
|
||||
bottom: 0.4em;
|
||||
}
|
||||
|
||||
#close-player {
|
||||
position: absolute;
|
||||
bottom: 320px;
|
||||
right: 0.5em;
|
||||
background-color: rgba(255,255,255,0.9);
|
||||
position: fixed;
|
||||
bottom: calc(360px + 0.2em - 3em);
|
||||
right: 0.7em;
|
||||
background-color: rgba(255, 255, 255, 1);
|
||||
width: 3em;
|
||||
height: 3em;
|
||||
border-radius: 1.5em;
|
||||
display: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#close-player img {
|
||||
position: relative;
|
||||
left: 50%;
|
||||
|
||||
54
static/stop.svg
Normal 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 |
@@ -143,6 +143,7 @@ $(document).ready(function () {
|
||||
"no_selected": (song.vote == -1) ? "selected" : "",
|
||||
"neutral_selected": (song.vote == 0) ? "selected" : "",
|
||||
"yes_selected": (song.vote == 1) ? "selected" : "",
|
||||
"play_button": (song.yt_id || song.spfy_id) ? "play" : "open",
|
||||
"categories": cats
|
||||
})).join('')
|
||||
|
||||
@@ -175,22 +176,30 @@ window.onSpotifyIframeApiReady = (IFrameAPI) => {
|
||||
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");
|
||||
$("#spotify-player").css("display", "none");
|
||||
$("#close-player").css("display", "none");
|
||||
$("#yt-player").html("");
|
||||
spotify_embed_controller.pause();
|
||||
|
||||
if (is_playing == id) {
|
||||
is_playing = -1;
|
||||
}
|
||||
|
||||
function play(id) {
|
||||
if (is_playing == id) {
|
||||
stop();
|
||||
} else {
|
||||
stop();
|
||||
|
||||
is_playing = id;
|
||||
|
||||
$("#song-" + id + " .cover-container .overlay img").attr("src", "/static/stop.svg");
|
||||
|
||||
song = all_songs[id];
|
||||
yt_id = song.yt_url.split('v=')[1]
|
||||
spotify_id = song.yt_url.split('/track/')[1]
|
||||
yt_id = song.yt_id
|
||||
spotify_id = song.spfy_id
|
||||
|
||||
if (yt_id) {
|
||||
$("#yt-player").css("display", "flex");
|
||||
@@ -205,15 +214,9 @@ function play(id) {
|
||||
spotify_embed_controller.play();
|
||||
}
|
||||
else {
|
||||
$("#yt-player").css("display", "none");
|
||||
$("#spotify-player").css("display", "none");
|
||||
$("#yt-player").html("");
|
||||
spotify_embed_controller.pause();
|
||||
window.open(song.yt_url, '_blank').focus();
|
||||
stop();
|
||||
$("#song-" + id + " .cover-container .overlay img").attr("src", "/static/open.svg");
|
||||
window.open(song.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
@@ -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;"> </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">< 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>
|
||||
|
||||
@@ -1,52 +1,164 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
|
||||
<head>
|
||||
<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/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.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;"> </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 type="text/javascript">
|
||||
var spotify_embed_controller;
|
||||
|
||||
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>
|
||||
<div id="songs"></div>
|
||||
<div id="spotify-player"><div id="spotify-embed"></div></div>
|
||||
<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>
|
||||
<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 class="song-data">VoicePlay: In The Air Tonight</div>
|
||||
<div class="categories" id="container">
|
||||
<span class="cat-1">Ballade</span><span class="cat-2">< 90er Remake</span><span class="cat-3">Something else </span><span class="cat-4">Something else </span>
|
||||
<div id="songs">
|
||||
{% for main_category, songs in songs_by_category.items() %}
|
||||
<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;"> </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 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 class="clear"></div>
|
||||
</div>-->
|
||||
</body>
|
||||
</html>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% 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>
|
||||