b39ac7212659fd8e6e2fe2a420880ce96817c543
[audio.git] / src / archive / views.py
1 from datetime import datetime
2 import os
3 import os.path
4 from urllib.parse import quote
5
6 from archive import settings
7 from django.contrib.auth import logout
8 from django.contrib.auth.decorators import permission_required
9 from django.urls import reverse
10 from django.db.models import Q, Max
11 from django.http import Http404, HttpResponse
12 from django.shortcuts import render, redirect, get_object_or_404
13 from django.views.decorators.http import require_POST
14
15 import mutagen
16
17 from archive.constants import status
18 from archive import models
19 from archive.forms import AudiobookForm
20 from archive import tasks
21 from archive.utils import all_files
22
23
24 def list_new(request):
25     division = 'new'
26
27     path = settings.NEW_PATH
28     objects = sorted(all_files(path))
29     return render(request, "archive/list_new.html", locals())
30
31
32 @permission_required('archive.change_audiobook')
33 def file_new(request, filename):
34     division = 'new'
35
36     filepath = filename
37     root_filepath = os.path.join(settings.NEW_PATH, filename)
38     if request.POST:
39         form = AudiobookForm(request.POST)
40         if form.is_valid():
41             try:
42                 form.save(path=filepath)
43             except IOError:
44                 raise Http404
45             return redirect(list_new)
46
47     try:
48         tags = mutagen.File(root_filepath)
49     except IOError:
50         raise Http404
51     d = {}
52     if tags:
53         for tag in tags:
54             value = tags[tag]
55             if isinstance(value, list):
56                 d[tag] = value[0]
57             else:
58                 d[tag] = value
59             if tag == 'project':
60                 try:
61                     d[tag] = models.Project.objects.get(name=d[tag]).pk
62                 except models.Project.DoesNotExist:
63                     d[tag] = None
64
65     if not request.POST:
66         form = AudiobookForm(initial=d)
67     return render(request, "archive/file_new.html", locals())
68
69
70 @require_POST
71 @permission_required('archive.change_audiobook')
72 def move_to_archive(request, filename):
73     """ move a new file to the unmanaged files dir """
74
75     filename_str = filename.encode('utf-8')
76     old_path = os.path.join(settings.NEW_PATH, filename_str)
77     new_path = os.path.join(settings.UNMANAGED_PATH, filename_str)
78     new_dir = os.path.split(new_path)[0]
79     if not os.path.isdir(new_dir):
80         os.makedirs(new_dir)
81
82     if not os.path.isfile(old_path):
83         raise Http404
84
85     try:
86         os.link(old_path, new_path)
87         os.unlink(old_path)
88     except OSError:
89         # destination file exists, don't overwrite it
90         # TODO: this should probably be more informative
91         return redirect(file_new, filename)
92
93     return redirect(list_new)
94
95
96 @require_POST
97 @permission_required('archive.change_audiobook')
98 def remove_to_archive(request, aid):
99     """ move a managed file to the unmanaged files dir """
100
101     audiobook = get_object_or_404(models.Audiobook, id=aid)
102     old_path = audiobook.source_file.path
103     new_path = os.path.join(settings.UNMANAGED_PATH,
104         str(audiobook.source_file)[len(settings.FILES_SAVE_PATH):].lstrip('/'))
105     new_dir = os.path.split(new_path)[0]
106     if not os.path.isdir(new_dir):
107         os.makedirs(new_dir)
108
109     if not os.path.isfile(old_path):
110         raise Http404
111
112     success = False
113     try_new_path = new_path
114     try_number = 0
115     while not success:
116         try:
117             os.link(old_path, try_new_path)
118         except OSError:
119             # destination file exists, don't overwrite it
120             try_number += 1
121             parts = new_path.rsplit('.', 1)
122             parts[0] += '_%d' % try_number
123             try_new_path = ".".join(parts)
124         else:
125             os.unlink(old_path)
126             audiobook.delete()
127             success = True
128
129     return redirect(list_unmanaged)
130
131 @require_POST
132 @permission_required('archive.change_audiobook')
133 def move_to_new(request, filename):
134     """ move a unmanaged file to new files dir """
135
136     filename_str = filename.encode('utf-8')
137     old_path = os.path.join(settings.UNMANAGED_PATH, filename_str)
138     new_path = os.path.join(settings.NEW_PATH, filename_str)
139     new_dir = os.path.split(new_path)[0]
140     if not os.path.isdir(new_dir):
141         os.makedirs(new_dir)
142
143     if not os.path.isfile(old_path):
144         raise Http404
145
146     try:
147         os.link(old_path, new_path)
148         os.unlink(old_path)
149     except OSError:
150         # destination file exists, don't overwrite it
151         # TODO: this should probably be more informative
152         return redirect(reverse(file_unmanaged, args=[filename]) + "?exists=1")
153
154     return redirect(list_unmanaged)
155
156
157 @require_POST
158 @permission_required('archive.change_audiobook')
159 def publish(request, aid, publish=True):
160     """ mark file for publishing """
161     audiobook = get_object_or_404(models.Audiobook, id=aid)
162     tags = {
163         'name': audiobook.title,
164         'url': audiobook.url,
165         'tags': audiobook.new_publish_tags(),
166         }
167     audiobook.set_mp3_tags(tags)
168     audiobook.set_ogg_tags(tags)
169     audiobook.mp3_status = audiobook.ogg_status = status.WAITING
170     audiobook.save()
171     # isn't there a race here?
172     audiobook.mp3_task = tasks.Mp3Task.delay(request.user.id, aid, publish).task_id
173     audiobook.ogg_task = tasks.OggTask.delay(request.user.id, aid, publish).task_id
174     audiobook.save()
175
176     return redirect(file_managed, aid)
177
178
179 @require_POST
180 @permission_required('archive.change_audiobook')
181 def cancel_publishing(request, aid):
182     """ cancel scheduled publishing """
183     audiobook = get_object_or_404(models.Audiobook, id=aid)
184     # TODO: cancel tasks
185     audiobook.mp3_status = None
186     audiobook.ogg_status = None
187     audiobook.youtube_status = None
188     audiobook.save()
189     return redirect(file_managed, aid)
190
191
192 def download(request, aid, which="source"):
193     if which not in ("source", "mp3", "ogg", 'mkv'):
194         raise Http404
195     audiobook = get_object_or_404(models.Audiobook, id=aid)
196     field = which
197     if which == 'mkv':
198         field = 'youtube'
199     file_ = getattr(audiobook, "%s_file" % field)
200     if not file_:
201         raise Http404
202     ext = file_.path.rsplit('.', 1)[-1]
203     response = HttpResponse(content_type='application/force-download')
204     
205     response['Content-Disposition'] = "attachment; filename*=UTF-8''%s.%s" % (
206         quote(audiobook.title.encode('utf-8'), safe=''), ext)
207     with open(file_.path, 'rb') as f:
208         response.write(f.read())
209     #response['X-Sendfile'] = file_.path.encode('utf-8')
210     return response
211
212
213 def list_unpublished(request):
214     division = 'unpublished'
215
216     objects = models.Audiobook.objects.filter(Q(mp3_published=None) | Q(ogg_published=None))
217     return render(request, "archive/list_unpublished.html", locals())
218
219
220 def list_publishing(request):
221     division = 'publishing'
222
223     objects = models.Audiobook.objects.exclude(mp3_status=None, ogg_status=None)
224     objects_by_status = {}
225     for o in objects:
226         if o.mp3_status:
227             k = o.mp3_status, o.get_mp3_status_display()
228             objects_by_status.setdefault(k, []).append(o)
229         if o.ogg_status and o.ogg_status != o.mp3_status:
230             k = o.ogg_status, o.get_ogg_status_display()
231             objects_by_status.setdefault(k, []).append(o)
232     status_objects = sorted(objects_by_status.items(), reverse=True)
233
234     return render(request, "archive/list_publishing.html", locals())
235
236
237 def list_published(request):
238     division = 'published'
239
240     objects = models.Audiobook.objects.exclude(Q(mp3_published=None) | Q(ogg_published=None))
241     return render(request, "archive/list_published.html", locals())
242
243
244 @permission_required('archive.change_audiobook')
245 def file_managed(request, id):
246     audiobook = get_object_or_404(models.Audiobook, id=id)
247
248     if request.POST:
249         form = AudiobookForm(request.POST, instance=audiobook)
250         if form.is_valid():
251             try:
252                 form.save()
253             except IOError:
254                 raise Http404
255
256     division = 'published' if audiobook.published() else 'unpublished'
257     path = audiobook.source_file.path[len(settings.FILES_PATH):].lstrip('/')
258
259     # for tags update
260     tags = mutagen.File(audiobook.source_file.path.encode('utf-8'))
261     if not tags:
262         tags = {}
263     form = AudiobookForm(instance=audiobook)
264
265     user_can_publish = (
266         request.user.is_authenticated and
267         request.user.oauthconnection_set.filter(access=True).exists())
268
269     return render(request, "archive/file_managed.html", locals())
270
271
272 def list_unmanaged(request):
273     division = 'unmanaged'
274
275     objects = sorted(all_files(settings.UNMANAGED_PATH))
276     return render(request, "archive/list_unmanaged.html", locals())
277
278
279 def file_unmanaged(request, filename):
280     division = 'unmanaged'
281
282     tags = mutagen.File(os.path.join(settings.UNMANAGED_PATH, filename.encode('utf-8')))
283     if not tags:
284         tags = {}
285     
286     err_exists = request.GET.get('exists')
287     return render(request, "archive/file_unmanaged.html", locals())