Fonctionalités de base

This commit is contained in:
Klafyvel 2018-03-25 17:52:52 +02:00
parent 762d736625
commit 5c30be2c65
12 changed files with 385 additions and 5 deletions

View file

@ -14,8 +14,9 @@ Including another URLconf
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('player.urls')),
]

24
player/forms.py Normal file
View file

@ -0,0 +1,24 @@
from urllib.parse import urlparse
import datetime
from django import forms
from player.models import Playlist, Link
class PlaylistForm(forms.ModelForm):
class Meta:
model = Playlist
fields = ['name']
def is_valid(self):
self.instance.date = datetime.datetime.now()
return super().is_valid()
class LinkForm(forms.Form):
url = forms.URLField(label="URL de la piste à ajouter")
def get_token(self):
p=urlparse(self.cleaned_data['url'])
print(p.query)
return [i for i in p.query.split('&') if i and i[0]=='v'][0].split('=')[-1]

View file

@ -0,0 +1,29 @@
# Generated by Django 2.0.3 on 2018-03-24 22:40
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('player', '0001_initial'),
]
operations = [
migrations.RemoveField(
model_name='link',
name='url',
),
migrations.AddField(
model_name='link',
name='token',
field=models.CharField(default='', max_length=200, verbose_name='Token'),
preserve_default=False,
),
migrations.AddField(
model_name='playlist',
name='name',
field=models.CharField(default='Nom', max_length=255, verbose_name='Nom de la playlist'),
preserve_default=False,
),
]

View file

@ -1,9 +1,14 @@
from django.db import models
from django.shortcuts import reverse
PK_LENGTH = 23
class Playlist(models.Model):
date = models.DateTimeField(verbose_name="date")
name = models.CharField(
max_length=255,
verbose_name="Nom de la playlist"
)
@staticmethod
def reverse_token(token):
@ -13,14 +18,17 @@ class Playlist(models.Model):
return int(self.date.timestamp()) << PK_LENGTH | self.pk
def get_absolute_url(self):
pass
return reverse('player:playlist', kwargs={'token':self.get_token()})
def __str__(self):
return "Playlist " + str(self.get_token())
class Link(models.Model):
url = models.URLField(verbose_name="Lien")
token = models.CharField(
max_length=200,
verbose_name="Token",
)
playlist = models.ForeignKey(
Playlist,
on_delete=models.CASCADE,

View file

@ -0,0 +1,30 @@
{% extends 'base.html' %}
{% block content %}
<div class="row">
<div class="col-md-6 text-justify">
<h1>Live Share</h1>
<br/>
<p>Bienvenue sur Live Share. Vous pouvez créer des playlist youtube anonymes, collaboratives et éphémères !</p>
<br/>
<br/>
<a class="btn btn-success btn-lg" type='button' href="{% url 'player:new' %}">
<i class="fa fa-plus"></i> Nouvelle playlist
</a>
<br/>
<br/>
</div>
<div class="col-md-6 text-justify">
<h1>Diffusions en cours</h1>
<br/>
{% for l in lists %}
<a class="card bg-secondary text-white" href="{{l.get_absolute_url}}">
<div class="card-body">
<i class="far fa-play-circle"></i>
{{l.name}}
</div>
</a>
<br/>
{% endfor %}
</div>
{% endblock %}

View file

@ -0,0 +1,164 @@
{% extends "base.html" %}
{% load bootstrap4 %}
{% block title %} - {{playlist.name}}{% endblock %}
{% block content %}
<div class="row">
<div class="col-md-3">
<h1>{{playlist.name}}</h1>
</div>
<div class="col-md-3">
<a class="btn btn-success btn-sm" type='button' href="">
<i class="fa fa-edit"></i> Changer le nom
</a>
</div>
<div class="col-md-6">
Lien de partage : <input class="input-monospace" value="{{request.get_host}}{{playlist.get_absolute_url}}" type="text" readonly="">
</div>
</div>
<div class="row" style="height:100%">
<div class="col-md-6">
<h2>Lecture</h2>
<!-- 1. The <iframe> (and video player) will replace this <div> tag. -->
<div id="player" style="max-width:100%"></div>
<form onSubmit="return addLink();" class="form" id="add_link_form">
{% csrf_token %}
{% bootstrap_form form %}
<button class="btn btn-outline-primary"><i class="fa fa-plus"></i> Ajouter</button>
</form>
<script>
var tracks = [
{% for link in playlist.link_set.all %}
"{{link.token}}",
{% endfor %}
];
// 2. This code loads the IFrame Player API code asynchronously.
var tag = document.createElement('script');
var current_link = -1;
tag.src = "https://www.youtube.com/iframe_api";
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
// 3. This function creates an <iframe> (and YouTube player)
// after the API code downloads.
var player;
function onYouTubeIframeAPIReady() {
player = new YT.Player('player', {
height: '390',
width: '640',
events: {
'onReady': onPlayerReady,
'onStateChange': onPlayerStateChange
}
});
}
// 4. The API will call this function when the video player is ready.
function onPlayerReady(event) {
console.log(current_link);
console.log(tracks.length);
if(tracks.length > 0 && current_link < (tracks.length - 1)) {
current_link += 1
player.loadVideoById(tracks[current_link]);
event.target.playVideo();
}
setInterval(loadLinks, 5000);
}
function onPlayerStateChange(event) {
if (event.data == YT.PlayerState.ENDED) {
onPlayerReady();
}
}
function stopVideo() {
player.stopVideo();
}
function addLink() {
var form = $('#add_link_form');
$.ajax({
type: "post",
url: "{% url 'player:add' playlist.get_token %}",
data: form.serialize(),
async: true,
success: loadLinks,
})
$('#id_url').val('');
return false;
}
function updateLinks(data) {
var links = document.getElementById("links");
while (links.firstChild) {
links.removeChild(links.firstChild);
}
var model = document.getElementById('link_template');
tracks = [];
for (var i=0; i<data.length; i++)
{
tracks.push(data[i].fields.token);
var card = model.cloneNode(true);
card.style.display = 'block';
card.id = 'link_' + data[i].fields.token;
card.getElementsByClassName('link_name')[0].innerHTML = data[i].fields.token;
links.appendChild(card);
links.append(document.createElement('br'));
}
if (current_link < 0) {
onPlayerReady();
}
}
function loadLinks() {
$.ajax({
type: "get",
url: "{% url 'player:list' playlist.get_token %}",
async: true,
success: updateLinks
})
}
</script>
</div>
<div class="col-md-6" style="height:100%; overflow-y:scroll;">
<h2>Pistes à venir</h2>
<div id="links">
{% for link in playlist.link_set.all %}
<div class="card bg-secondary text-white" href="{{l.get_absolute_url}}" id="link_{{link.token}}">
<div class="card-body">
<div class="row container">
<div class="col-md-6">
<i class="far fa-play-circle"></i>
{{link.token}}
</div>
<div class="col-md-3">
</div>
<div class="col-md-3 btn-group">
<a class="btn btn-outline-light" href="#"><i class="fas fa-thumbs-up"></i></a>
<a class="btn btn-outline-light" href="#"><i class="fas fa-thumbs-down"></i></a>
<a class="btn btn-outline-light" href="#"><i class="fas fa-trash"></i></a>
</div>
</div>
</div>
</div>
<br/>
{% endfor %}
</div>
</div>
</div>
<div style="display:none;" class="card bg-secondary text-white" href="{{l.get_absolute_url}}" id="link_template">
<div class="card-body">
<div class="row container">
<div class="col-md-6">
<i class="far fa-play-circle"></i>
<span class="link_name"></span>
</div>
<div class="col-md-3">
</div>
<div class="col-md-3 btn-group">
<a class="btn btn-outline-light" href="#"><i class="fas fa-thumbs-up"></i></a>
<a class="btn btn-outline-light" href="#"><i class="fas fa-thumbs-down"></i></a>
<a class="btn btn-outline-light" href="#"><i class="fas fa-trash"></i></a>
</div>
</div>
</div>
</div>
{% endblock %}

12
player/urls.py Normal file
View file

@ -0,0 +1,12 @@
from django.urls import path
from . import views
app_name = "player"
urlpatterns = [
path('<int:token>', views.playlist, name='playlist'),
path('remove/<int:pk>', views.remove_link, name='remove-link'),
path('<int:token>/add', views.add_link, name='add'),
path('<int:token>/list', views.get_list, name='list'),
path('new', views.new_playlist, name='new'),
path('', views.all_playlist, name='all'),
]

View file

@ -1,3 +1,65 @@
from django.shortcuts import render
import datetime
# Create your views here.
from django.shortcuts import render, get_object_or_404, redirect
from django.http import HttpResponse
from django.views.decorators.csrf import csrf_exempt
from django.core.serializers import serialize
from player.models import Playlist, Link
from player.forms import PlaylistForm, LinkForm
def new_playlist(request):
p = PlaylistForm(request.POST or None)
if p.is_valid():
playlist = p.save()
return redirect(playlist.get_absolute_url())
return render(request, 'form.html', {
'form':p,
'validate':'Créer',
'title':'Nouvelle playlist'
})
@csrf_exempt
def get_list(request, token):
p = get_object_or_404(Playlist, pk=Playlist.reverse_token(token))
s = serialize('json', p.link_set.all(), fields=('token',))
return HttpResponse(s, content_type='application/json')
@csrf_exempt
def add_link(request, token):
p = get_object_or_404(Playlist, pk=Playlist.reverse_token(token))
l = LinkForm(request.POST or None)
if l.is_valid():
yt_token = l.get_token()
link = Link()
link.token = yt_token
link.playlist = p
link.save()
return HttpResponse('Ok')
return render(request, 'form_inline.html', {
'form':l,
'validate':'Ajouter'
})
def remove_link(request, pk):
l = get_object_or_404(Link, pk=pk)
l.delete()
return HttpResponse('Ok')
def playlist(request, token):
p = get_object_or_404(Playlist, pk=Playlist.reverse_token(token))
add_link_form = LinkForm()
return render(request, 'player/playlist.html', {
'playlist':p,
'form':add_link_form
})
def all_playlist(request):
p = Playlist.objects.all().order_by('date')
return render(request, 'player/all_list.html', {'lists':p})

View file

@ -0,0 +1,27 @@
{% load bootstrap4 %}
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<title>LSY{% block title %}{% endblock %}</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>
<script defer src="https://use.fontawesome.com/releases/v5.0.6/js/all.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
</head>
<body>
{% include 'navbar.html' %}
<main role="main" class="container">
<br/>
{% bootstrap_messages %}
{% block content %}{% endblock %}
</main>
<hr/>
<footer class="footer container text-center text-muted">
LSY by Klafyvel
</footer>
</body>
</html>

14
templates/form.html Normal file
View file

@ -0,0 +1,14 @@
{% extends 'base.html' %}
{% load bootstrap4 %}
{% block title %} - {{title}}{%endblock%}
{% block content %}
<h3>{{title}}</h3>
<form action="" method="post" class="form">
{% csrf_token %}
{% bootstrap_form form %}
<button class="btn btn-outline-primary"><i class="fa fa-star"></i> {{validate}}</button>
</form>
<br>
<br>
<br>
{% endblock %}

View file

@ -0,0 +1,6 @@
{% load bootstrap4 %}
<form action="" method="post" class="form" id="{{id}}">
{% csrf_token %}
{% bootstrap_form form %}
<button class="btn btn-outline-primary"><i class="fa fa-star"></i> {{validate}}</button>
</form>

View file

@ -0,0 +1,3 @@
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
<a class="navbar-brand" href="/"><i class="fa fa-users"></i> LSY</a>
</nav>