Fonctionalités de base
This commit is contained in:
parent
762d736625
commit
5c30be2c65
12 changed files with 385 additions and 5 deletions
|
@ -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
24
player/forms.py
Normal 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]
|
||||
|
29
player/migrations/0002_auto_20180324_2340.py
Normal file
29
player/migrations/0002_auto_20180324_2340.py
Normal 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,
|
||||
),
|
||||
]
|
|
@ -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,
|
||||
|
|
30
player/templates/player/all_list.html
Normal file
30
player/templates/player/all_list.html
Normal 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 %}
|
164
player/templates/player/playlist.html
Normal file
164
player/templates/player/playlist.html
Normal 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
12
player/urls.py
Normal 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'),
|
||||
]
|
|
@ -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})
|
||||
|
|
|
@ -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
14
templates/form.html
Normal 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 %}
|
6
templates/form_inline.html
Normal file
6
templates/form_inline.html
Normal 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>
|
|
@ -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>
|
Loading…
Reference in a new issue