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)
248 if audiobook.source_file:
249 path = audiobook.source_file.path[len(settings.FILES_PATH):].lstrip('/')
252 tags = mutagen.File(audiobook.source_file.path.encode('utf-8'))
255 form = AudiobookForm(instance=audiobook)
258 request.user.is_authenticated and
259 request.user.oauthconnection_set.filter(access=True).exists())
262 parts_count = audiobook.parts_count
264 series = models.Audiobook.objects.filter(slug=audiobook.slug)
265 if not audiobook.index:
266 alerts.append(_('There is more than one part, but index is not set.'))
267 if set(series.values_list('index', flat=True)) != set(range(1, parts_count + 1)):
268 alerts.append(_('Part indexes are not 1..%(parts_count)d.') % {"parts_count": parts_count})
270 from youtube.models import YouTube
271 youtube = YouTube.objects.first()
272 youtube_title = youtube.get_title(audiobook)
273 youtube_description = youtube.get_description(audiobook)
276 return render(request, "archive/file_managed.html", locals())
279 def list_unmanaged(request):
280 objects = sorted(all_files(settings.UNMANAGED_PATH))
281 return render(request, "archive/list_unmanaged.html", locals())
284 def file_unmanaged(request, filename):
285 tags = mutagen.File(os.path.join(settings.UNMANAGED_PATH, filename.encode('utf-8')))
289 err_exists = request.GET.get('exists')
290 return render(request, "archive/file_unmanaged.html", locals())
293 class BookView(ListView):
294 template_name = 'archive/book.html'
296 def get_queryset(self):
297 return models.Audiobook.objects.filter(slug=self.kwargs["slug"]).order_by(