12 changed files with 385 additions and 5 deletions
@ -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] |
|||
|
|||
@ -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, |
|||
), |
|||
] |
|||
@ -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 %} |
|||
@ -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 %} |
|||
@ -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> |
|||
@ -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 %} |
|||
@ -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 new issue