]> git.0d.be Git - django-panik-emissions.git/commitdiff
add new "regie" page, to assist in producing an episode
authorFrédéric Péters <fpeters@0d.be>
Sat, 7 May 2016 13:42:12 +0000 (15:42 +0200)
committerFrédéric Péters <fpeters@0d.be>
Sat, 7 May 2016 13:42:12 +0000 (15:42 +0200)
emissions/migrations/0007_playlistelement.py [new file with mode: 0644]
emissions/models.py
emissions/templates/emissions/episode_regie.html [new file with mode: 0644]
emissions/urls.py
emissions/views.py

diff --git a/emissions/migrations/0007_playlistelement.py b/emissions/migrations/0007_playlistelement.py
new file mode 100644 (file)
index 0000000..0eec020
--- /dev/null
@@ -0,0 +1,32 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import models, migrations
+import emissions.models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('emissions', '0006_auto_20160507_1315'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='PlaylistElement',
+            fields=[
+                ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
+                ('title', models.CharField(max_length=200, verbose_name='Title')),
+                ('notes', models.CharField(default=b'', max_length=200, verbose_name='Notes', blank=True)),
+                ('sound', models.FileField(upload_to=emissions.models.get_playlist_sound_path, max_length=250, verbose_name='Sound')),
+                ('order', models.PositiveIntegerField()),
+                ('episode', models.ForeignKey(to='emissions.Episode', null=True)),
+            ],
+            options={
+                'ordering': ['order'],
+                'verbose_name': 'Playlist Element',
+                'verbose_name_plural': 'Playlist Elements',
+            },
+            bases=(models.Model,),
+        ),
+    ]
index be32d88c0dff93111fed073d121d64a2107f2dbd..9808b90b6fbcedd545ea75d8b494a3c8e67debb3 100644 (file)
@@ -634,6 +634,27 @@ class Focus(models.Model):
         return None
 
 
+def get_playlist_sound_path(instance, filename):
+    return os.path.join('playlists', instance.episode.emission.slug,
+            instance.episode.slug, os.path.basename(filename))
+
+
+class PlaylistElement(models.Model):
+    episode = models.ForeignKey('Episode', null=True)
+    title = models.CharField(_('Title'), max_length=200)
+    notes = models.CharField(_('Notes'), max_length=200, blank=True, default='')
+    sound = models.FileField(_('Sound'), upload_to=get_playlist_sound_path, max_length=250)
+    order = models.PositiveIntegerField()
+
+    class Meta:
+        verbose_name = _('Playlist Element')
+        verbose_name_plural = _('Playlist Elements')
+        ordering = ['order']
+
+    def shortcut(self):
+        return chr(ord('a')+self.order-1)
+
+
 @receiver(pre_save, sender=Focus, dispatch_uid='focus_pre_save')
 def set_focus_on_save(sender, instance, **kwargs):
     object = instance.get_related_object()
diff --git a/emissions/templates/emissions/episode_regie.html b/emissions/templates/emissions/episode_regie.html
new file mode 100644 (file)
index 0000000..dff0d52
--- /dev/null
@@ -0,0 +1,107 @@
+{% extends "base.html" %}
+{% load soundfiles panikdbtags %}
+{% block appbar %}
+<h2>{{ episode.emission.title }} — {{ episode.title }}</h2>
+<span><a href="../">Retourner à l'épisode</a></span>
+{% endblock %}
+
+{% block content %}
+
+<p>
+<a class="big-friendly-button start-episode" href="#">Démarrer l'émission</a>
+<a class="big-friendly-button end-episode" href="#">Terminer l'émission</a>
+<a style="display: none" class="big-friendly-button reset-episode" href="#">Réinitialiser</a>
+</p>
+
+<input type="hidden" id="start_time">
+<input type="hidden" id="end_time">
+
+<table id="playlist"
+  data-update-order-url="{% url 'episode-regie-update-order' emission_slug=episode.emission.slug slug=episode.slug %}"
+     ><tbody>
+{% for element in playlist %}
+<tr data-element-id="{{element.id}}" data-shortcut="{{element.shortcut}}">
+    <td class="handle">⠿⁣</td>
+    <td class="playlist-title">{{element.title}} <span class="shortcut">[{{element.shortcut}}]</span></td>
+    <td><audio src="{{element.sound.url}}" controls/></td>
+    <td>{{element.notes}}</td>
+  </tr>
+{% endfor %}
+</tbody></table>
+
+{% if can_manage %}
+<hr />
+<h3>Ajouter à la playlist</h3>
+<form id="add-playlist-elements-form" method="POST" action="." enctype="multipart/form-data">
+{% csrf_token %}
+<input type="file" name="playlist-element" multiple>
+</form>
+{% endif %}
+
+{% endblock %}
+
+{% block page-end %}
+<script>
+$(function() {
+  $('#start_time').val('');
+  $('#end_time').val('');
+  $('#add-playlist-elements-form input[type=file]').on('change', function() {
+    $('body').addClass('submitting');
+    $(this).parent().submit();
+  });
+  $('#playlist tbody').sortable({
+    handle: '.handle',
+    update: function(event, ui) {
+      var new_order = Array();
+      $('#playlist tr').each(function(i, tr) {
+        new_order.push($(tr).data('element-id'));
+      });
+      $.ajax({
+         url: $('#playlist').data('update-order-url'),
+         data: {'new-order': new_order}
+      });
+    }
+  });
+  $('.playlist-title').on('click', function() {
+     var audio = $(this).parent().find('audio')[0];
+     if (audio.paused === true) {
+       audio.play();
+       if ($('#start_time').val() == "") {
+         $('.start-episode').trigger('click');
+       }
+     } else {
+       audio.pause();
+     }
+  });
+  $(document).keypress(function(ev) {
+    $('[data-shortcut=' + ev.key + '] .playlist-title').trigger('click');
+    return true;
+  });
+  $('.start-episode').click(function() {
+    $('#start_time').val(Date.now());
+    $(this).toggleClass('button-done');
+    $('.reset-episode').show();
+    return false;
+  });
+  $('.end-episode').click(function() {
+    $('#end_time').val(Date.now());
+    $(this).toggleClass('button-done');
+    return false;
+  });
+  $('.reset-episode').click(function() {
+    $('#start_time').val('');
+    $('#end_time').val('');
+    $('a').removeClass('button-done');
+    $(this).hide();
+    return false;
+  });
+  $('audio').each(function(i, elem) {
+    elem.addEventListener('play', function() {
+       if ($('#start_time').val() == "") {
+         $('.start-episode').trigger('click');
+       }
+    });
+  });
+});
+</script>
+{% endblock %}
index fa42a1cacc7ab06b8969e723a0d6579966c03998..0940b28ac59d9731eae903d92a2b24ab9442efcc 100644 (file)
@@ -43,6 +43,10 @@ urlpatterns = patterns('',
     url(r'^(?P<emission_slug>[\w,-]+)/(?P<slug>[\w,-]+)/sounds/(?P<pk>\d+)/$',
             SoundFileUpdateView.as_view(), name='soundfile-update'),
 
+    url(r'^(?P<emission_slug>[\w,-]+)/(?P<slug>[\w,-]+)/regie/$', EpisodeRegieView.as_view(), name='episode-regie'),
+    url(r'^(?P<emission_slug>[\w,-]+)/(?P<slug>[\w,-]+)/regie/update-order$',
+        EpisodeRegieUpdateOrderView.as_view(), name='episode-regie-update-order'),
+
     url(r'^(?P<emission_slug>[\w,-]+)/add-newsitem$',
             EmissionNewsItemAdd.as_view(), name='emission-newsitem-add'),
 
index ae1551d39551eb63000de05014ee324243fe1118..1bc87590dc1957fc4032bb9562a1c1365d762f20 100644 (file)
@@ -5,7 +5,7 @@ import os
 from django.core.exceptions import PermissionDenied
 from django.core.files.storage import DefaultStorage
 from django.core.urlresolvers import reverse_lazy
-from django.http import HttpResponse, Http404
+from django.http import HttpResponse, Http404, HttpResponseRedirect
 from django.core.urlresolvers import reverse, reverse_lazy
 from django.shortcuts import redirect
 from django.utils.translation import ugettext as _, ugettext_lazy
@@ -21,7 +21,8 @@ from django.contrib import messages
 import haystack.views
 
 from .models import Emission, Episode, Diffusion, Category, Schedule, \
-                    SoundFile, NewsItem, NewsCategory, Absence
+                    SoundFile, NewsItem, NewsCategory, Absence, \
+                    PlaylistElement
 from .forms import EmissionForm, EpisodeForm, EpisodeNewForm, ScheduleForm, \
                    DiffusionForm, SoundFileForm, NewsItemForm, \
                    SoundFileEditForm, AbsenceForm
@@ -39,7 +40,9 @@ __all__ = ['EmissionListView', 'EmissionDetailView', 'EmissionCreateView',
            'NewsItemDeleteView', 'NewsItemAddView',
            'SoundFileDeleteView', 'SoundFileUpdateView',
            'EmissionAddAbsenceView', 'AbsenceDeleteView',
-           'EmissionOpenChatView', 'EmissionCloseChatView']
+           'EmissionOpenChatView', 'EmissionCloseChatView',
+           'EpisodeRegieView',
+           'EpisodeRegieUpdateOrderView']
 
 
 SUCCESS_MESSAGE = ugettext_lazy('Your changes will appear online in a few minutes.')
@@ -554,3 +557,47 @@ class EmissionCloseChatView(DetailView):
         self.object.chat_open = None
         self.object.save()
         return redirect(self.object.get_absolute_url())
+
+
+class EpisodeRegieView(DetailView):
+    model = Episode
+    template_name = 'emissions/episode_regie.html'
+
+    def get_object(self, queryset=None):
+        try:
+            return Episode.objects.get(slug=self.kwargs.get('slug'),
+                    emission__slug=self.kwargs.get('emission_slug'))
+        except Episode.DoesNotExist:
+            raise Http404()
+
+    def post(self, request, *args, **kwargs):
+        episode = self.get_object()
+        i = PlaylistElement.objects.filter(episode=episode).count()
+        for upload in request.FILES.getlist('playlist-element'):
+            playlist_element = PlaylistElement(
+                    episode=episode,
+                    title=upload.name,
+                    sound=upload,
+                    order=i+1)
+            playlist_element.save()
+            i += 1
+        messages.info(request, _('%d files uploaded successfully.') %
+                len(request.FILES.getlist('playlist-element')))
+        return HttpResponseRedirect('.')
+
+    def get_context_data(self, **kwargs):
+        context = super(EpisodeRegieView, self).get_context_data(**kwargs)
+        context['playlist'] = PlaylistElement.objects.filter(episode=self.object.id)
+        try:
+            context['can_manage'] = self.request.user.can_manage(self.object)
+        except AttributeError:
+            pass
+        return context
+
+class EpisodeRegieUpdateOrderView(View):
+    def get(self, request, *args, **kwargs):
+        new_order = request.GET.getlist('new-order[]')
+        for element in PlaylistElement.objects.filter(id__in=new_order):
+            element.order = new_order.index(str(element.id)) + 1
+            element.save()
+        return HttpResponse('ok')