]> git.0d.be Git - panikdb.git/blob - panikdb/regie/views.py
add download link to ytdl tracks
[panikdb.git] / panikdb / regie / views.py
1 import datetime
2 import glob
3 import json
4 import logging
5 import os
6 import subprocess
7
8 import requests
9 from django.conf import settings
10 from django.contrib import messages
11 from django.contrib.auth.forms import AuthenticationForm
12 from django.core.files.storage import default_storage
13 from django.db.models import Q
14 from django.http import HttpResponseForbidden, HttpResponseNotAllowed, HttpResponseRedirect, JsonResponse
15 from django.urls import reverse
16 from django.utils.translation import ugettext_lazy as _
17 from django.views.generic.base import TemplateView
18 from nonstop.models import SomaLogLine, Track
19
20 from emissions.models import Nonstop
21 from panikdb.context_processors import internal_ip
22 from panikdb.service_messages.models import Message
23
24 from .models import PigeEmailRequest
25
26 logger = logging.getLogger('panikdb')
27
28
29 def is_internal(request):
30     return internal_ip(request).get('internal_ip')
31
32
33 def is_regie_computer(request):
34     return internal_ip(request).get('current_ip') in settings.REGIE_IPS
35
36
37 class RegieHome(TemplateView):
38     template_name = 'regie-home.html'
39
40     def get_context_data(self, **kwargs):
41         context = super().get_context_data(**kwargs)
42         context['login_form'] = AuthenticationForm()
43
44         context['service_message'] = Message.objects.all().first()
45
46         if settings.PIGE_DOWNLOAD_BASE_URL:
47             context['has_pige'] = True
48             context['pige_minimum_date'] = datetime.datetime.now() - datetime.timedelta(days=100)
49             context['pige_hours'] = ['%02d' % x for x in range(24)]
50             context['pige_minutes'] = ['%02d' % x for x in range(0, 60, 15)]
51
52         if getattr(settings, 'NONSTOP_ON_AIR_SWITCH_URL', None) and getattr(self, 'request', None):
53             context['is_regie_computer'] = is_regie_computer(self.request)
54             context['has_switch'] = True
55
56         return context
57
58
59 regie_home = RegieHome.as_view()
60
61
62 def pige_post(request, *args, **kwargs):
63     if request.method != 'POST':
64         return HttpResponseNotAllowed(['POST'])
65     # studio2-20160515-13h45-14h00m05.flac
66     name = '%(src)s-%(date)s-%(start_hour)sh%(start_min)s-%(end_hour)sh%(end_min)s' % {
67         'src': request.POST['src'],
68         'date': request.POST['date'].replace('-', ''),
69         'start_hour': request.POST['start_hour'],
70         'start_min': request.POST['start_min'],
71         'end_hour': request.POST['end_hour'],
72         'end_min': request.POST['end_min'],
73     }
74     if is_internal(request):
75         name = name + '.wav'
76     else:
77         name = name + '.ogg'
78     if 'sendmail' in request.POST:
79         email = request.POST['email']
80         PigeEmailRequest.objects.create(email=email, download_spec=name)
81         messages.info(
82             request,
83             _(
84                 'An email with a download link will soon be sent to %s, '
85                 'please be patient, it may take up to 20 minutes to arrive.'
86             )
87             % email,
88         )
89         return HttpResponseRedirect(reverse('regie-home'))
90     else:  # direct download
91         return HttpResponseRedirect(settings.PIGE_DOWNLOAD_BASE_URL + '/' + name)
92
93
94 def json_tracks(tracks):
95     return JsonResponse(
96         {
97             'data': [
98                 {
99                     'id': track.id,
100                     'title': track.title,
101                     'artist': track.artist.name if track.artist_id else None,
102                     'instru': track.instru,
103                     'language': track.language,
104                     'duration': '%d:%02d'
105                     % (track.duration.total_seconds() / 60, track.duration.total_seconds() % 60),
106                 }
107                 for track in tracks
108             ]
109         }
110     )
111
112
113 def regie_tracks(request):
114     now = datetime.datetime.now()
115     current_zone = None
116     for nonstop in Nonstop.objects.all():
117         if (nonstop.start < nonstop.end and (now.time() >= nonstop.start and now.time() < nonstop.end)) or (
118             nonstop.start > nonstop.end and (now.time() >= nonstop.start or now.time() < nonstop.end)
119         ):
120             current_zone = nonstop
121             break
122
123     tracks = Track.objects.filter(duration__isnull=False)
124     tracks = tracks.filter(duration__gte=datetime.timedelta(minutes=2, seconds=30))
125     tracks = tracks.filter(duration__lt=datetime.timedelta(minutes=4, seconds=30))
126     if current_zone:
127         tracks = tracks.filter(nonstop_zones__in=[current_zone.id])
128     tracks = tracks.exclude(language__isnull=True).exclude(language='')
129     tracks = tracks.order_by('?')
130     return json_tracks(tracks[:5])
131
132
133 def regie_tracks_search(request):
134     q = request.GET['q']
135     if q.startswith('https://'):
136         ytdl_path = default_storage.path('ytdl')
137         if not os.path.exists(ytdl_path):
138             os.mkdir(ytdl_path)
139         for param in ('--dump-json', '--print-json'):
140             # make a first run with just metadata, this is to obtain the filename
141             # and use a file that was previously downloaded.
142             proc = subprocess.run(
143                 [
144                     'youtube-dl',
145                     '--extract-audio',
146                     '--no-playlist',
147                     '--no-mtime',
148                     param,
149                     '-o',
150                     ytdl_path + '/%(id)s.%(ext)s',
151                     q,
152                 ],
153                 capture_output=True,
154             )
155             output = json.loads(proc.stdout)
156             filename = output['_filename']
157             # audio conversion may be done by youtube-dl and filename would still
158             # contain the original filename, look for files with same basename,
159             # regardeless of the extension.
160             base_filename = os.path.splitext(filename)[0]
161             try:
162                 real_filename = [x for x in glob.glob(base_filename + '*') if not x.endswith('.part')][0]
163             except IndexError:
164                 real_filename = filename
165             else:
166                 break
167         if not os.path.exists(real_filename):
168             return JsonResponse({'err': 1})
169         title = output['title']
170         duration = '%d:%02d' % (int(output['duration']) / 60, int(output['duration']) % 60)
171         url = os.path.join(settings.MEDIA_URL, 'ytdl', real_filename[len(ytdl_path) + 1 :])
172         good_filename = '%s%s' % (title, os.path.splitext(real_filename)[-1])
173         return JsonResponse(
174             {'data': [{'title': title, 'duration': duration, 'url': url, 'filename': good_filename}]}
175         )
176
177     tracks = Track.objects.filter(duration__isnull=False).filter(
178         Q(title__icontains=q.lower()) | Q(artist__name__icontains=q.lower())
179     )
180     tracks = tracks.order_by('?')
181     return json_tracks(tracks[:5])
182
183
184 def playing(request):
185     latest_played = SomaLogLine.objects.select_related('track', 'track__artist').latest('play_timestamp')
186     duration = latest_played.track.duration
187     elapsed = datetime.datetime.now() - latest_played.play_timestamp
188     remaining = duration - elapsed
189     if remaining.total_seconds() < 2:
190         return JsonResponse({})
191     elif remaining.total_seconds() < 0:
192         remaining = datetime.timedelta(seconds=0)
193     response = {}
194     if latest_played.track.artist:
195         response['artist'] = latest_played.track.artist.name
196     response['title'] = latest_played.track.title
197     response['duration'] = duration.seconds
198     response['elapsed'] = int(elapsed.total_seconds())
199     return JsonResponse(response)
200
201
202 def switch(request, *args, **kwargs):
203     if not is_regie_computer(request):
204         return HttpResponseForbidden()
205     logger.info('regie switch to %s', request.GET['selection'])
206     requests.post(settings.NONSTOP_ON_AIR_SWITCH_URL, data={'s': request.GET['selection']})
207     return JsonResponse({'err': 0})