290 lines
9.2 KiB
Python
290 lines
9.2 KiB
Python
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)
|