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