1 from datetime import datetime
4 from urllib.parse import quote
6 from archive import settings
7 from django.contrib.auth.decorators import permission_required
8 from django.urls import reverse
9 from django.db.models import Q, Max
10 from django.http import Http404, HttpResponse
11 from django.shortcuts import render, redirect, get_object_or_404
12 from django.utils.translation import gettext as _
13 from django.views.decorators.http import require_POST
14 from django.views.generic import ListView
18 from archive.constants import status
19 from archive import models
20 from archive.forms import AudiobookForm
21 from archive import tasks
22 from archive.utils import all_files
25 def list_new(request):
28 path = settings.NEW_PATH
29 objects = sorted(all_files(path))
30 return render(request, "archive/list_new.html", locals())
33 @permission_required('archive.change_audiobook')
34 def file_new(request, filename):
38 root_filepath = os.path.join(settings.NEW_PATH, filename)
40 form = AudiobookForm(request.POST)
43 form.save(path=filepath)
46 return redirect(list_new)
49 tags = mutagen.File(root_filepath)
56 if isinstance(value, list):
62 d[tag] = models.Project.objects.get(name=d[tag]).pk
63 except models.Project.DoesNotExist:
67 form = AudiobookForm(initial=d)
68 return render(request, "archive/file_new.html", locals())
72 @permission_required('archive.change_audiobook')
73 def move_to_archive(request, filename):
74 """ move a new file to the unmanaged files dir """
76 filename_str = filename.encode('utf-8')
77 old_path = os.path.join(settings.NEW_PATH, filename_str)
78 new_path = os.path.join(settings.UNMANAGED_PATH, filename_str)
79 new_dir = os.path.split(new_path)[0]
80 if not os.path.isdir(new_dir):
83 if not os.path.isfile(old_path):
87 os.link(old_path, new_path)
90 # destination file exists, don't overwrite it
91 # TODO: this should probably be more informative
92 return redirect(file_new, filename)
94 return redirect(list_new)
98 @permission_required('archive.change_audiobook')
99 def remove_to_archive(request, aid):
100 """ move a managed file to the unmanaged files dir """
102 audiobook = get_object_or_404(models.Audiobook, id=aid)
103 old_path = audiobook.source_file.path
104 new_path = os.path.join(settings.UNMANAGED_PATH,
105 str(audiobook.source_file)[len(settings.FILES_SAVE_PATH):].lstrip('/'))
106 new_dir = os.path.split(new_path)[0]
107 if not os.path.isdir(new_dir):
110 if not os.path.isfile(old_path):
114 try_new_path = new_path
118 os.link(old_path, try_new_path)
120 # destination file exists, don't overwrite it
122 parts = new_path.rsplit('.', 1)
123 parts[0] += '_%d' % try_number
124 try_new_path = ".".join(parts)
130 return redirect(list_unmanaged)
133 @permission_required('archive.change_audiobook')
134 def move_to_new(request, filename):
135 """ move a unmanaged file to new files dir """
137 filename_str = filename.encode('utf-8')
138 old_path = os.path.join(settings.UNMANAGED_PATH, filename_str)
139 new_path = os.path.join(settings.NEW_PATH, filename_str)
140 new_dir = os.path.split(new_path)[0]
141 if not os.path.isdir(new_dir):
144 if not os.path.isfile(old_path):
148 os.link(old_path, new_path)
151 # destination file exists, don't overwrite it
152 # TODO: this should probably be more informative
153 return redirect(reverse(file_unmanaged, args=[filename]) + "?exists=1")
155 return redirect(list_unmanaged)
159 @permission_required('archive.change_audiobook')
160 def publish(request, aid, publish=True):
161 """ mark file for publishing """
162 audiobook = get_object_or_404(models.Audiobook, id=aid)
164 'name': audiobook.title,
165 'url': audiobook.url,
166 'tags': audiobook.new_publish_tags(),
168 audiobook.set_mp3_tags(tags)
169 audiobook.set_ogg_tags(tags)
170 audiobook.mp3_status = audiobook.ogg_status = status.WAITING
172 # isn't there a race here?
173 audiobook.mp3_task = tasks.Mp3Task.delay(request.user.id, aid, publish).task_id
174 audiobook.ogg_task = tasks.OggTask.delay(request.user.id, aid, publish).task_id
177 return redirect(file_managed, aid)
181 @permission_required('archive.change_audiobook')
182 def cancel_publishing(request, aid):
183 """ cancel scheduled publishing """
184 audiobook = get_object_or_404(models.Audiobook, id=aid)
186 audiobook.mp3_status = None
187 audiobook.ogg_status = None
188 audiobook.youtube_status = None
190 return redirect(file_managed, aid)
193 def download(request, aid, which="source"):
194 if which not in ("source", "mp3", "ogg", 'mkv'):
196 audiobook = get_object_or_404(models.Audiobook, id=aid)
200 file_ = getattr(audiobook, "%s_file" % field)
203 ext = file_.path.rsplit('.', 1)[-1]
204 response = HttpResponse(content_type='application/force-download')
206 response['Content-Disposition'] = "attachment; filename*=UTF-8''%s.%s" % (
207 quote(audiobook.title.encode('utf-8'), safe=''), ext)
208 with open(file_.path, 'rb') as f:
209 response.write(f.read())
210 #response['X-Sendfile'] = file_.path.encode('utf-8')
214 def list_unpublished(request):
215 division = 'unpublished'
217 objects = models.Audiobook.objects.filter(Q(mp3_published=None) | Q(ogg_published=None))
218 return render(request, "archive/list_unpublished.html", locals())
221 def list_publishing(request):
222 division = 'publishing'
224 objects = models.Audiobook.objects.exclude(mp3_status=None, ogg_status=None, youtube_status=None)
225 objects_by_status = {}
229 statuses.add((o.mp3_status, o.get_mp3_status_display()))
231 statuses.add((o.ogg_status, o.get_ogg_status_display()))
233 statuses.add((o.youtube_status, o.get_youtube_status_display()))
234 for status in statuses:
235 objects_by_status.setdefault(status, []).append(o)
236 status_objects = sorted(objects_by_status.items(), reverse=True)
238 return render(request, "archive/list_publishing.html", locals())
241 def list_published(request):
242 division = 'published'
244 objects = models.Audiobook.objects.exclude(Q(mp3_published=None) | Q(ogg_published=None))
245 return render(request, "archive/list_published.html", locals())
248 @permission_required('archive.change_audiobook')
249 def file_managed(request, id):
250 audiobook = get_object_or_404(models.Audiobook, id=id)
253 form = AudiobookForm(request.POST, instance=audiobook)
260 division = 'published' if audiobook.published() else 'unpublished'
261 path = audiobook.source_file.path[len(settings.FILES_PATH):].lstrip('/')
264 tags = mutagen.File(audiobook.source_file.path.encode('utf-8'))
267 form = AudiobookForm(instance=audiobook)
270 request.user.is_authenticated and
271 request.user.oauthconnection_set.filter(access=True).exists())
274 parts_count = audiobook.parts_count
276 series = models.Audiobook.objects.filter(slug=audiobook.slug)
277 if not audiobook.index:
278 alerts.append(_('There is more than one part, but index is not set.'))
279 if set(series.values_list('index', flat=True)) != set(range(1, parts_count + 1)):
280 alerts.append(_('Part indexes are not 1..%(parts_count)d.') % {"parts_count": parts_count})
282 return render(request, "archive/file_managed.html", locals())
285 def list_unmanaged(request):
286 division = 'unmanaged'
288 objects = sorted(all_files(settings.UNMANAGED_PATH))
289 return render(request, "archive/list_unmanaged.html", locals())
292 def file_unmanaged(request, filename):
293 division = 'unmanaged'
295 tags = mutagen.File(os.path.join(settings.UNMANAGED_PATH, filename.encode('utf-8')))
299 err_exists = request.GET.get('exists')
300 return render(request, "archive/file_unmanaged.html", locals())
303 class BookView(ListView):
304 template_name = 'archive/book.html'
306 def get_queryset(self):
307 return models.Audiobook.objects.filter(slug=self.kwargs['slug'])