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
189 audiobook.youtube_queued = None
191 return redirect(file_managed, aid)
194 def download(request, aid, which="source"):
195 if which not in ("source", "mp3", "ogg", 'mkv'):
197 audiobook = get_object_or_404(models.Audiobook, id=aid)
201 file_ = getattr(audiobook, "%s_file" % field)
204 ext = file_.path.rsplit('.', 1)[-1]
205 response = HttpResponse(content_type='application/force-download')
207 response['Content-Disposition'] = "attachment; filename*=UTF-8''%s.%s" % (
208 quote(audiobook.title.encode('utf-8'), safe=''), ext)
209 with open(file_.path, 'rb') as f:
210 response.write(f.read())
211 #response['X-Sendfile'] = file_.path.encode('utf-8')
215 def list_unpublished(request):
216 division = 'unpublished'
218 objects = models.Audiobook.objects.filter(Q(mp3_published=None) | Q(ogg_published=None))
219 return render(request, "archive/list_unpublished.html", locals())
222 def list_publishing(request):
223 division = 'publishing'
225 objects = models.Audiobook.objects.exclude(
226 mp3_status=None, ogg_status=None, youtube_status=None
227 ).order_by("youtube_queued", "title")
228 objects_by_status = {}
232 statuses.add((o.mp3_status, o.get_mp3_status_display()))
234 statuses.add((o.ogg_status, o.get_ogg_status_display()))
236 statuses.add((o.youtube_status, o.get_youtube_status_display()))
237 for status in statuses:
238 objects_by_status.setdefault(status, []).append(o)
239 status_objects = sorted(objects_by_status.items(), reverse=True)
241 return render(request, "archive/list_publishing.html", locals())
244 def list_published(request):
245 division = 'published'
247 objects = models.Audiobook.objects.exclude(Q(mp3_published=None) | Q(ogg_published=None))
248 return render(request, "archive/list_published.html", locals())
251 @permission_required('archive.change_audiobook')
252 def file_managed(request, id):
253 audiobook = get_object_or_404(models.Audiobook, id=id)
256 form = AudiobookForm(request.POST, instance=audiobook)
263 division = 'published' if audiobook.published() else 'unpublished'
264 path = audiobook.source_file.path[len(settings.FILES_PATH):].lstrip('/')
267 tags = mutagen.File(audiobook.source_file.path.encode('utf-8'))
270 form = AudiobookForm(instance=audiobook)
273 request.user.is_authenticated and
274 request.user.oauthconnection_set.filter(access=True).exists())
277 parts_count = audiobook.parts_count
279 series = models.Audiobook.objects.filter(slug=audiobook.slug)
280 if not audiobook.index:
281 alerts.append(_('There is more than one part, but index is not set.'))
282 if set(series.values_list('index', flat=True)) != set(range(1, parts_count + 1)):
283 alerts.append(_('Part indexes are not 1..%(parts_count)d.') % {"parts_count": parts_count})
285 return render(request, "archive/file_managed.html", locals())
288 def list_unmanaged(request):
289 division = 'unmanaged'
291 objects = sorted(all_files(settings.UNMANAGED_PATH))
292 return render(request, "archive/list_unmanaged.html", locals())
295 def file_unmanaged(request, filename):
296 division = 'unmanaged'
298 tags = mutagen.File(os.path.join(settings.UNMANAGED_PATH, filename.encode('utf-8')))
302 err_exists = request.GET.get('exists')
303 return render(request, "archive/file_unmanaged.html", locals())
306 class BookView(ListView):
307 template_name = 'archive/book.html'
309 def get_queryset(self):
310 return models.Audiobook.objects.filter(slug=self.kwargs['slug'])