musik/game/views.py

290 lines
9.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import random
import google.oauth2.credentials
import google_auth_oauthlib
import googleapiclient.discovery
from django.conf import settings
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.auth.models import User
from django.contrib.messages.views import SuccessMessageMixin
from django.db.models import Count, Q
from django.http import JsonResponse
from django.shortcuts import get_object_or_404, redirect
from django.views import View
from django.views.generic.detail import DetailView, SingleObjectMixin
from django.views.generic.edit import CreateView, DeleteView, UpdateView
from . import forms, models, utils
class OwnerFilterMixin(LoginRequiredMixin):
def get_queryset(self):
return super().get_queryset().filter(owner=self.request.user)
class MemberFilterMixin(LoginRequiredMixin):
def get_queryset(self):
return (
super()
.get_queryset()
.filter(Q(members=self.request.user) | Q(owner=self.request.user))
.distinct()
)
class GroupMixin:
model = models.Group
fields = ["name"]
class GroupCreateView(LoginRequiredMixin, GroupMixin, CreateView):
def form_valid(self, form):
form.instance.owner = self.request.user
return super().form_valid(form)
class GroupUpdateView(OwnerFilterMixin, GroupMixin, UpdateView):
pass
class GroupDeleteView(OwnerFilterMixin, GroupMixin, SuccessMessageMixin, DeleteView):
success_url = "/"
success_message = "Le groupe a été supprimé avec succès."
class GroupDetailView(MemberFilterMixin, GroupMixin, DetailView):
def get_context_data(self, **kwargs):
data = super().get_context_data(**kwargs)
data["musics"] = data["group"].musicvideo_set.filter(owner=self.request.user)
data["owner_count"] = (
data["group"]
.musicvideo_set.filter(owner=data["group"].owner, blacklisted=False)
.count()
)
data["members"] = data["group"].members.annotate(
count=Count(
"musicvideo",
filter=Q(group=data["group"], musicvideo__blacklisted=False),
)
)
return data
class GroupAddMusicView(MemberFilterMixin, SingleObjectMixin, View):
model = models.Group
def post(self, request, pk):
group = self.get_object()
yt_id = request.POST.get("yt_id")
if not yt_id:
return JsonResponse({"error": "You must provide a YouTube ID."}, status=400)
yt_id = utils.parse_musik(yt_id)
title = utils.get_yt_title(yt_id)
if not title:
return JsonResponse({"error": "Invalid YouTube ID."}, status=400)
group.musicvideo_set.create(yt_id=yt_id, title=title, owner=request.user)
group.save()
return redirect(group)
class GroupAddMemberView(OwnerFilterMixin, SingleObjectMixin, View):
model = models.Group
def post(self, request, pk):
group = self.get_object()
username = request.POST.get("username")
user = User.objects.get(username=username)
group.members.add(user)
return redirect(group)
class GroupRemoveMusicView(OwnerFilterMixin, SingleObjectMixin, View):
model = models.MusicVideo
def get(self, request, pk):
music = self.get_object()
group = music.group
music.delete()
return redirect(group)
class GroupRemoveMemberView(View):
def get(self, request, pk, user_pk):
relation = get_object_or_404(
models.Group.members.through,
group_id=pk,
user_id=user_pk,
group__owner=request.user,
)
group = relation.group
relation.delete()
return redirect(group)
class GroupRemoveGameView(SingleObjectMixin, SuccessMessageMixin, View):
model = models.MusikGame
success_message = "Le jeu du %(date)s a été supprimé avec succès."
def get_queryset(self):
return super().get_queryset().filter(group__owner=self.request.user)
def get(self, request, pk):
game = self.get_object()
group = game.group
game.delete()
return redirect(group)
class GameCreateView(LoginRequiredMixin, CreateView):
model = models.MusikGame
form_class = forms.MusikGameForm
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs["group"] = self.kwargs["pk"]
return kwargs
def get_context_data(self, **kwargs):
data = super().get_context_data(**kwargs)
data["group"] = models.Group.objects.filter(owner=self.request.user).get(
pk=self.kwargs["pk"]
)
return data
def form_valid(self, form):
group = models.Group.objects.get(pk=self.kwargs["pk"])
form.instance.group = group
res = super().form_valid(form)
players = []
musics = []
for player in form.instance.players.all():
players += 2 * [player]
musics += random.sample(
list(
player.musicvideo_set.filter(group=group, blacklisted=False).all()
),
form.instance.n,
)
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 creds := self.request.user.youtubecredentials:
credentials = google.oauth2.credentials.Credentials(**creds.credentials)
yt_api = googleapiclient.discovery.build(
"youtube", "v3", credentials=credentials
)
pl_request = yt_api.playlists().insert(
part="snippet,status",
body={
"snippet": {
"title": f"Musik {group.name} {form.instance.date.strftime('%x')}",
"description": "Playlist générée par Musik",
},
"status": {
"privacyStatus": "private",
},
},
)
pl_response = pl_request.execute()
pl_id = pl_response.get("id")
form.instance.playlist = pl_id
form.instance.save()
for _, music in pm_list:
request = yt_api.playlistItems().insert(
part="snippet",
body={
"snippet": {
"playlistId": pl_id,
"resourceId": {
"kind": "youtube#video",
"videoId": music.yt_id,
},
}
},
)
request.execute()
return res
class GameDetailView(LoginRequiredMixin, DetailView):
model = models.MusikGame
def get_queryset(self):
return (
super()
.get_queryset()
.filter(
Q(group__members=self.request.user) | Q(group__owner=self.request.user)
)
.distinct()
)
class YoutubeLoginView(LoginRequiredMixin, View):
def get(self, request):
flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file(
settings.YOUTUBE_OAUTH_SECRETS,
["https://www.googleapis.com/auth/youtube.force-ssl"],
)
flow.redirect_uri = "https://localhost/youtube_callback/"
auth_url, state = flow.authorization_url(
access_type="offline",
include_granted_scopes="true",
prompt="consent",
)
self.request.session["state"] = state
return redirect(auth_url)
class YoutubeCallbackView(LoginRequiredMixin, View):
def get(self, request):
if request.GET.get("error"):
return redirect("/")
print(request.GET)
state = self.request.session.get("state")
flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file(
settings.YOUTUBE_OAUTH_SECRETS,
["https://www.googleapis.com/auth/youtube.force-ssl"],
state=state,
)
flow.redirect_uri = "https://localhost/youtube_callback/"
flow.fetch_token(code=request.GET.get("code"))
credentials = flow.credentials
models.YoutubeCredentials.objects.update_or_create(
user=request.user,
defaults={
"credentials": {
"token": credentials.token,
"refresh_token": credentials.refresh_token,
"token_uri": credentials.token_uri,
"client_id": credentials.client_id,
"client_secret": credentials.client_secret,
"granted_scopes": credentials.granted_scopes,
}
},
)
return redirect("/")
class GroupClearBlacklistView(OwnerFilterMixin, SingleObjectMixin, View):
model = models.Group
def get(self, request, pk):
group = self.get_object()
group.musicvideo_set.filter(blacklisted=True).update(blacklisted=False)
return redirect(group)