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):
26 path = settings.NEW_PATH
27 objects = sorted(all_files(path))
28 return render(request, "archive/list_new.html", locals())
31 @permission_required('archive.change_audiobook')
32 def file_new(request, filename):
34 root_filepath = os.path.join(settings.NEW_PATH, filename)
36 form = AudiobookForm(request.POST)
39 form.save(path=filepath)
42 return redirect(list_new)
45 tags = mutagen.File(root_filepath)
52 if isinstance(value, list):
58 d[tag] = models.Project.objects.get(name=d[tag]).pk
59 except models.Project.DoesNotExist:
63 form = AudiobookForm(initial=d)
64 return render(request, "archive/file_new.html", locals())
68 @permission_required('archive.change_audiobook')
69 def move_to_archive(request, filename):
70 """ move a new file to the unmanaged files dir """
72 filename_str = filename.encode('utf-8')
73 old_path = os.path.join(settings.NEW_PATH, filename_str)
74 new_path = os.path.join(settings.UNMANAGED_PATH, filename_str)
75 new_dir = os.path.split(new_path)[0]
76 if not os.path.isdir(new_dir):
79 if not os.path.isfile(old_path):
83 os.link(old_path, new_path)
86 # destination file exists, don't overwrite it
87 # TODO: this should probably be more informative
88 return redirect(file_new, filename)
90 return redirect(list_new)
94 @permission_required('archive.change_audiobook')
95 def remove_to_archive(request, aid):
96 """ move a managed file to the unmanaged files dir """
98 audiobook = get_object_or_404(models.Audiobook, id=aid)
99 old_path = audiobook.source_file.path
100 new_path = os.path.join(settings.UNMANAGED_PATH,
101 str(audiobook.source_file)[len(settings.FILES_SAVE_PATH):].lstrip('/'))
102 new_dir = os.path.split(new_path)[0]
103 if not os.path.isdir(new_dir):
106 if not os.path.isfile(old_path):
110 try_new_path = new_path
114 os.link(old_path, try_new_path)
116 # destination file exists, don't overwrite it
118 parts = new_path.rsplit('.', 1)
119 parts[0] += '_%d' % try_number
120 try_new_path = ".".join(parts)
126 return redirect(list_unmanaged)
129 @permission_required('archive.change_audiobook')
130 def move_to_new(request, filename):
131 """ move a unmanaged file to new files dir """
133 filename_str = filename.encode('utf-8')
134 old_path = os.path.join(settings.UNMANAGED_PATH, filename_str)
135 new_path = os.path.join(settings.NEW_PATH, filename_str)
136 new_dir = os.path.split(new_path)[0]
137 if not os.path.isdir(new_dir):
140 if not os.path.isfile(old_path):
144 os.link(old_path, new_path)
147 # destination file exists, don't overwrite it
148 # TODO: this should probably be more informative
149 return redirect(reverse(file_unmanaged, args=[filename]) + "?exists=1")
151 return redirect(list_unmanaged)
155 @permission_required('archive.change_audiobook')
156 def publish(request, aid, publish=True):
157 """ mark file for publishing """
158 audiobook = get_object_or_404(models.Audiobook, id=aid)
160 'name': audiobook.title,
161 'url': audiobook.url,
162 'tags': audiobook.new_publish_tags(),
164 audiobook.set_mp3_tags(tags)
165 audiobook.set_ogg_tags(tags)
166 audiobook.mp3_status = audiobook.ogg_status = status.WAITING
168 # isn't there a race here?
169 audiobook.mp3_task = tasks.Mp3Task.delay(request.user.id, aid, publish).task_id
170 audiobook.ogg_task = tasks.OggTask.delay(request.user.id, aid, publish).task_id
173 return redirect(file_managed, aid)
177 @permission_required('archive.change_audiobook')
178 def cancel_publishing(request, aid):
179 """ cancel scheduled publishing """
180 audiobook = get_object_or_404(models.Audiobook, id=aid)
182 audiobook.mp3_status = None
183 audiobook.ogg_status = None
184 audiobook.youtube_status = None
185 audiobook.youtube_queued = None
187 return redirect(file_managed, aid)
190 def download(request, aid, which="source"):
191 if which not in ("source", "mp3", "ogg", 'mkv'):
193 audiobook = get_object_or_404(models.Audiobook, id=aid)
197 file_ = getattr(audiobook, "%s_file" % field)
200 ext = file_.path.rsplit('.', 1)[-1]
201 response = HttpResponse(content_type='application/force-download')
203 response['Content-Disposition'] = "attachment; filename*=UTF-8''%s.%s" % (
204 quote(audiobook.title.encode('utf-8'), safe=''), ext)
205 with open(file_.path, 'rb') as f:
206 response.write(f.read())
207 #response['X-Sendfile'] = file_.path.encode('utf-8')
211 def list_publishing(request):
212 objects = models.Audiobook.objects.exclude(
213 mp3_status=None, ogg_status=None, youtube_status=None
214 ).order_by("youtube_queued", "title")
215 objects_by_status = {}
219 statuses.add((o.mp3_status, o.get_mp3_status_display()))
221 statuses.add((o.ogg_status, o.get_ogg_status_display()))
223 statuses.add((o.youtube_status, o.get_youtube_status_display()))
224 for status in statuses:
225 objects_by_status.setdefault(status, []).append(o)
226 status_objects = sorted(objects_by_status.items(), reverse=True)
228 return render(request, "archive/list_publishing.html", locals())
231 class AudiobookList(ListView):
232 queryset = models.Audiobook.objects.all()
235 @permission_required('archive.change_audiobook')
236 def file_managed(request, id):
237 audiobook = get_object_or_404(models.Audiobook, id=id)
240 form = AudiobookForm(request.POST, instance=audiobook)
247 path = audiobook.source_file.path[len(settings.FILES_PATH):].lstrip('/')
250 tags = mutagen.File(audiobook.source_file.path.encode('utf-8'))
253 form = AudiobookForm(instance=audiobook)
256 request.user.is_authenticated and
257 request.user.oauthconnection_set.filter(access=True).exists())
260 parts_count = audiobook.parts_count
262 series = models.Audiobook.objects.filter(slug=audiobook.slug)
263 if not audiobook.index:
264 alerts.append(_('There is more than one part, but index is not set.'))
265 if set(series.values_list('index', flat=True)) != set(range(1, parts_count + 1)):
266 alerts.append(_('Part indexes are not 1..%(parts_count)d.') % {"parts_count": parts_count})
268 return render(request, "archive/file_managed.html", locals())
271 def list_unmanaged(request):
272 objects = sorted(all_files(settings.UNMANAGED_PATH))
273 return render(request, "archive/list_unmanaged.html", locals())
276 def file_unmanaged(request, filename):
277 tags = mutagen.File(os.path.join(settings.UNMANAGED_PATH, filename.encode('utf-8')))
281 err_exists = request.GET.get('exists')
282 return render(request, "archive/file_unmanaged.html", locals())
285 class BookView(ListView):
286 template_name = 'archive/book.html'
288 def get_queryset(self):
289 return models.Audiobook.objects.filter(slug=self.kwargs["slug"]).order_by(