Compare commits
13 commits
Author | SHA1 | Date | |
---|---|---|---|
8c2e62e8de | |||
e4168b474d | |||
b20eee8cfb | |||
a462fabde4 | |||
b9711cbe9c | |||
bd8529cd01 | |||
84c432c325 | |||
c639307cfb | |||
951128147c | |||
0b8ce65a0a | |||
22bb6931e8 | |||
cb3518a5e5 | |||
e6d757c069 |
23 changed files with 155 additions and 61 deletions
|
@ -77,11 +77,6 @@ article.message {
|
|||
}
|
||||
|
||||
#hero {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: radial-gradient(
|
||||
circle 50vh at calc(100vw - 4rem) 50%,
|
||||
var(--pico-primary-background),
|
||||
|
@ -90,19 +85,41 @@ article.message {
|
|||
color-mix(in hsl, var(--pico-primary-background) 30%, var(--pico-background-color)) 60%,
|
||||
color-mix(in hsl, var(--pico-primary-background) 10%, var(--pico-background-color)) 80%,
|
||||
var(--pico-background-color));
|
||||
display: grid;
|
||||
grid-template-rows: 1fr min-content;
|
||||
align-items: center;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
padding: 4rem;
|
||||
|
||||
.big-logo {
|
||||
font-size: 8rem;
|
||||
main {
|
||||
display: contents;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 4rem;
|
||||
section {
|
||||
max-width: 20rem;
|
||||
}
|
||||
}
|
||||
.full-page {
|
||||
height: 100%;
|
||||
display: grid;
|
||||
grid-template-rows: 1fr;
|
||||
align-items: center;
|
||||
margin-bottom: 4rem;
|
||||
|
||||
&.r {
|
||||
-ms-grid-column-align: end;
|
||||
}
|
||||
.big-logo {
|
||||
font-size: 8rem;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 4rem;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
h1,
|
||||
h2,
|
||||
|
@ -201,3 +218,7 @@ table.results, table.musics {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.brand-name {
|
||||
color: var(--pico-primary);
|
||||
}
|
||||
|
|
BIN
base/static/favicon/apple-touch-icon.png
Normal file
BIN
base/static/favicon/apple-touch-icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.9 KiB |
BIN
base/static/favicon/favicon-96x96.png
Normal file
BIN
base/static/favicon/favicon-96x96.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.6 KiB |
BIN
base/static/favicon/favicon.ico
Normal file
BIN
base/static/favicon/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
25
base/static/favicon/favicon.svg
Normal file
25
base/static/favicon/favicon.svg
Normal file
|
@ -0,0 +1,25 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="256" height="256" version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<defs>
|
||||
<linearGradient id="Gradient" x1="0" x2="0" y1="0" y2="1">
|
||||
<stop offset="0%" style="stop-color:#AA40BF;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#AA40BF;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
<filter id="alpha-to-white">
|
||||
<feColorMatrix in="SourceGraphic" type="matrix"
|
||||
values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"/>
|
||||
</filter>
|
||||
<g id="child-svg"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" height="16px" width="16px"><path d="M18.7134 8.12811L18.4668 8.69379C18.2864 9.10792 17.7136 9.10792 17.5331 8.69379L17.2866 8.12811C16.8471 7.11947 16.0555 6.31641 15.0677 5.87708L14.308 5.53922C13.8973 5.35653 13.8973 4.75881 14.308 4.57612L15.0252 4.25714C16.0384 3.80651 16.8442 2.97373 17.2761 1.93083L17.5293 1.31953C17.7058 0.893489 18.2942 0.893489 18.4706 1.31953L18.7238 1.93083C19.1558 2.97373 19.9616 3.80651 20.9748 4.25714L21.6919 4.57612C22.1027 4.75881 22.1027 5.35653 21.6919 5.53922L20.9323 5.87708C19.9445 6.31641 19.1529 7.11947 18.7134 8.12811ZM7 3H12V6H9V17C9 19.2091 7.20914 21 5 21C2.79086 21 1 19.2091 1 17C1 14.7909 2.79086 13 5 13C5.72857 13 6.41165 13.1948 7 13.5351V3ZM18 13.5351V11H20V17C20 19.2091 18.2091 21 16 21C13.7909 21 12 19.2091 12 17C12 14.7909 13.7909 13 16 13C16.7286 13 17.4117 13.1948 18 13.5351Z" /></svg></g>
|
||||
</defs>
|
||||
<rect
|
||||
width="256"
|
||||
height="256"
|
||||
fill="url(#Gradient)"
|
||||
ry="128"
|
||||
x="0"
|
||||
y="0" />
|
||||
<use xlink:href="#child-svg" filter="url(#alpha-to-white)"
|
||||
transform="matrix(8,0,0,8,64,64)" />
|
||||
</svg>
|
After Width: | Height: | Size: 1.7 KiB |
21
base/static/favicon/site.webmanifest
Normal file
21
base/static/favicon/site.webmanifest
Normal file
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"name": "MyWebSite",
|
||||
"short_name": "MySite",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/web-app-manifest-192x192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable"
|
||||
},
|
||||
{
|
||||
"src": "/web-app-manifest-512x512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable"
|
||||
}
|
||||
],
|
||||
"theme_color": "#aa40bf",
|
||||
"background_color": "#ffffff",
|
||||
"display": "standalone"
|
||||
}
|
BIN
base/static/favicon/web-app-manifest-192x192.png
Normal file
BIN
base/static/favicon/web-app-manifest-192x192.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 9.4 KiB |
BIN
base/static/favicon/web-app-manifest-512x512.png
Normal file
BIN
base/static/favicon/web-app-manifest-512x512.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 47 KiB |
|
@ -11,7 +11,7 @@
|
|||
Musik
|
||||
{% endblock title %}
|
||||
</title>
|
||||
<link rel="icon" href="{% static "logo.svg" %}">
|
||||
{% include "favicon.html" %}
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Exo:ital,wght@0,100..900;1,100..900&display=swap"
|
||||
|
|
8
base/templates/favicon.html
Normal file
8
base/templates/favicon.html
Normal file
|
@ -0,0 +1,8 @@
|
|||
{% load static %}
|
||||
<meta name="theme-color" content="#aa40bf">
|
||||
<link rel="icon" type="image/png" href="{% static "favicon/favicon-96x96.png" %}" sizes="96x96">
|
||||
<link rel="icon" type="image/svg+xml" href="{% static "favicon/favicon.svg" %}">
|
||||
<link rel="shortcut icon" href="{% static "favicon/favicon.ico" %}">
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="{% static "favicon/apple-touch-icon.png" %}">
|
||||
<meta name="apple-mobile-web-app-title" content="Musik">
|
||||
<link rel="manifest" href="{% static "favicon/site.webmanifest" %}">
|
|
@ -1 +1 @@
|
|||
Musik {{ VERSION }} – © <a href="https://code.edgarpierre.fr/edpibu/musik">Edgar P. Burkhart</a> – <a href="{% url "legal" %}">Mentions légales</a>
|
||||
Musik {{ VERSION }} – © <a href="https://code.edgarpierre.fr/edpibu/musik">Edgar P. Burkhart</a> – <a href="{% url "legal" %}">Mentions légales et confidentialité</a>
|
||||
|
|
|
@ -1,11 +1,25 @@
|
|||
{% load static %}
|
||||
<div id="hero">
|
||||
<main>
|
||||
<i class="ri-music-ai-fill big-logo"></i>
|
||||
<h1>Musik</h1>
|
||||
<p>
|
||||
<a href="{% url "home" %}" role="button"><i class="ri-play-fill"></i> Jouer</a>
|
||||
</p>
|
||||
<div class="full-page">
|
||||
<div>
|
||||
<i class="ri-music-ai-fill big-logo"></i>
|
||||
<h1>Musik</h1>
|
||||
<p>
|
||||
<a href="{% url "home" %}" role="button"><i class="ri-play-fill"></i> Jouer</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="full-page r">
|
||||
<section>
|
||||
<h2>
|
||||
<span class="brand-name"><i class="ri-music-ai-fill"></i> Musik</span> Le jeu où ta playlist devient ton arme secrète !
|
||||
</h2>
|
||||
<p>
|
||||
Invite ta bande, ajoute tes sons fétiches, et c’est parti ! Une playlist Youtube apparaît, mélangeant les coups de cœur de tout le monde. Le jeu ? Écoute, devine qui a choisi quoi, et découvre les secrets musicaux de tes potes. Entre pièges, révélations et fous rires, Musik c’est le jeu parfait pour tester vos oreilles… et vos amitiés. Prêt à jouer le DJ incognito ?
|
||||
</p>
|
||||
</section>
|
||||
</div>
|
||||
</main>
|
||||
<footer>
|
||||
{% include "footer.html" %}
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
Youtube est une marque de Google LLC.
|
||||
</p>
|
||||
<p>
|
||||
La suppression des données stockée par le service Musik pour son utilisation peut être demandée par email à <a href="mailto:contact@edgarpierre.fr">Edgar P. Burkhart</a>.
|
||||
Les données saisies dans Musik (groupes créés, listes de musiques) sont conservées jusqu'à demande de suppression. La suppression du compte entraîne la suppression de l'ensemble des données qui y sont liées.
|
||||
La suppression du compte peut être demandée dans les paramètres du compte. La suppression des données est immédiate et définitive.
|
||||
</p>
|
||||
{% endblock content %}
|
||||
|
|
|
@ -32,10 +32,12 @@ services:
|
|||
rabbitmq:
|
||||
image: rabbitmq
|
||||
container_name: musik_rabbitmq
|
||||
restart: unless-stopped
|
||||
|
||||
postgres:
|
||||
image: postgres:17
|
||||
container_name: musik_postgres
|
||||
restart: unless-stopped
|
||||
env_file: stack.env
|
||||
volumes:
|
||||
- /docker/musik/postgres:/var/lib/postgresql/data
|
||||
|
|
|
@ -99,6 +99,9 @@ def generateYoutubePlaylist(sender, instance, created, **kwargs):
|
|||
|
||||
if creds := instance.group.owner.youtubecredentials:
|
||||
tasks.generate_playlist.delay_on_commit(creds.credentials, instance.pk)
|
||||
else:
|
||||
instance.playlist_loading = False
|
||||
instance.save()
|
||||
|
||||
|
||||
@receiver(post_delete, sender=MusikGame)
|
||||
|
@ -118,13 +121,10 @@ class MusicGameOrder(models.Model):
|
|||
value = models.PositiveIntegerField(default=0)
|
||||
|
||||
def update_value(self):
|
||||
n_right = self.musicgameanswer_set.filter(game__player=F("answer")).count()
|
||||
if n_right == 0:
|
||||
self.value = 1000
|
||||
else:
|
||||
self.value = 1000 / (
|
||||
1 + ((n_right - 1) / (self.game.players.count() - 1)) ** 0.5
|
||||
)
|
||||
x = self.musicgameanswer_set.filter(game__player=F("answer")).count()
|
||||
n = self.game.players.count()
|
||||
n = max(3, n)
|
||||
self.value = 1000 * 2 ** (-(x - 2) / (n - 2))
|
||||
self.save()
|
||||
|
||||
class Meta:
|
||||
|
|
|
@ -18,7 +18,7 @@ def generate_playlist(creds, game_pk):
|
|||
"description": "Playlist générée par Musik",
|
||||
},
|
||||
"status": {
|
||||
"privacyStatus": "private",
|
||||
"privacyStatus": "unlisted",
|
||||
},
|
||||
},
|
||||
)
|
||||
|
|
|
@ -21,6 +21,6 @@
|
|||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% if not musikgame.over %}<button type="submit">Valider mes réponses</button>{% endif %}
|
||||
{% if not musikgame.over %}<button type="submit">Sauvegarder mes réponses</button>{% endif %}
|
||||
</form>
|
||||
{% endblock content %}
|
||||
|
|
|
@ -9,31 +9,31 @@
|
|||
{% endif %}
|
||||
{{ musikgame.date }}
|
||||
</h1>
|
||||
{% if musikgame.playlist or musikgame.playlist_loading %}
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
<fieldset role="group">
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
<fieldset role="group">
|
||||
{% if musikgame.playlist or musikgame.playlist_loading %}
|
||||
<a target="_blank"
|
||||
href="{% yt_playlist musikgame %}"
|
||||
role="button"
|
||||
{% if musikgame.playlist_loading %}aria-busy="true"{% endif %}><i class="ri-youtube-fill"></i> Playlist</a>
|
||||
{% if musikgame.over %}
|
||||
<a href="{% url "game_answer" musikgame.pk %}"
|
||||
role="button"
|
||||
class="secondary"><i class="ri-play-list-2-fill"></i> Mes réponses</a>
|
||||
{% else %}
|
||||
<a href="{% url "game_answer" musikgame.pk %}" role="button"><i class="ri-play-list-2-fill"></i> Répondre</a>
|
||||
{% endif %}
|
||||
</fieldset>
|
||||
{% if is_leader and not musikgame.over %}
|
||||
<fieldset>
|
||||
<button type="submit" formaction="{% url "game_end" musikgame.pk %}">
|
||||
<i class="ri-stop-circle-fill"></i> Finir la partie
|
||||
</button>
|
||||
</fieldset>
|
||||
{% endif %}
|
||||
</form>
|
||||
{% endif %}
|
||||
{% if musikgame.over %}
|
||||
<a href="{% url "game_answer" musikgame.pk %}"
|
||||
role="button"
|
||||
class="secondary"><i class="ri-play-list-2-fill"></i> Mes réponses</a>
|
||||
{% else %}
|
||||
<a href="{% url "game_answer" musikgame.pk %}" role="button"><i class="ri-play-list-2-fill"></i> Répondre</a>
|
||||
{% endif %}
|
||||
</fieldset>
|
||||
{% if is_leader and not musikgame.over %}
|
||||
<fieldset>
|
||||
<button type="submit" formaction="{% url "game_end" musikgame.pk %}">
|
||||
<i class="ri-stop-circle-fill"></i> Finir la partie
|
||||
</button>
|
||||
</fieldset>
|
||||
{% endif %}
|
||||
</form>
|
||||
<h2>
|
||||
<i class="ri-group-2-fill"></i> Joueurs
|
||||
</h2>
|
||||
|
|
|
@ -5,10 +5,12 @@
|
|||
<i class="ri-group-2-fill"></i> {{ group.name }}
|
||||
</h1>
|
||||
<p>
|
||||
{% if not user.youtubecredentials.credentials %}
|
||||
<a href="{% url "youtube_login" %}" role="button"><i class="ri-youtube-fill"></i> Me connecter au compte Youtube</a>
|
||||
{% if group.owner.youtubecredentials.credentials %}
|
||||
<i class="ri-youtube-fill"></i> Une playlist sera générée automatiquement sur le compte Youtube de <strong>{{ group.owner }}</strong> (<strong>{{ group.owner.youtubecredentials.title }}</strong>).
|
||||
{% elif user == group.owner %}
|
||||
<a href="{% url "youtube_login" %}" role="button"><i class="ri-youtube-fill"></i> Connecter mon compte Youtube</a>
|
||||
{% else %}
|
||||
<i class="ri-youtube-fill"></i> Une playlist sera générée automatiquement sur le compte Youtube <strong>{{ user.youtubecredentials.title }}</strong>.
|
||||
<small>Aucune playlist Youtube ne sera générée car <strong>{{ group.owner }}</strong> n'a pas lié son compte Youtube.</small>
|
||||
{% endif %}
|
||||
</p>
|
||||
{% form form %}
|
||||
|
|
|
@ -327,15 +327,12 @@ class GameCreateView(LoginRequiredMixin, CreateView):
|
|||
pm_list = list(zip(players, musics))
|
||||
random.shuffle(pm_list)
|
||||
for (player, music), order in zip(pm_list, range(1, len(pm_list) + 1)):
|
||||
music.blacklisted = True
|
||||
music.save()
|
||||
models.MusicGameOrder.objects.create(
|
||||
game=form.instance, player=player, music_video=music, order=order
|
||||
)
|
||||
|
||||
if models.YoutubeCredentials.objects.filter(user=self.request.user).exists():
|
||||
form.instance.playlist_loading = True
|
||||
form.instance.save()
|
||||
form.instance.playlist_loading = True
|
||||
form.instance.save()
|
||||
return res
|
||||
|
||||
|
||||
|
@ -481,6 +478,9 @@ class GameEndView(LoginRequiredMixin, SingleObjectMixin, View):
|
|||
if not game.group.is_leader(request.user):
|
||||
raise PermissionDenied()
|
||||
game.over = True
|
||||
models.MusicVideo.objects.filter(musicgameorder__game=game).update(
|
||||
blacklisted=True
|
||||
)
|
||||
|
||||
for go in game.musicgameorder_set.all():
|
||||
go.update_value()
|
||||
|
|
|
@ -13,7 +13,7 @@ https://docs.djangoproject.com/en/5.2/ref/settings/
|
|||
import os
|
||||
from pathlib import Path
|
||||
|
||||
VERSION = "0.4.1"
|
||||
VERSION = "0.4.4"
|
||||
|
||||
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
||||
BASE_DIR = Path(__file__).resolve().parent.parent
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[project]
|
||||
name = "musik"
|
||||
version = "0.4.1"
|
||||
version = "0.4.4"
|
||||
description = "Le jeu de Musik."
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.12"
|
||||
|
|
2
uv.lock
generated
2
uv.lock
generated
|
@ -423,7 +423,7 @@ wheels = [
|
|||
|
||||
[[package]]
|
||||
name = "musik"
|
||||
version = "0.4.1"
|
||||
version = "0.4.4"
|
||||
source = { virtual = "." }
|
||||
dependencies = [
|
||||
{ name = "celery" },
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue