+@vary_on_auth # Because of 'liked'.
+class BookList(ListAPIView):
+ permission_classes = [DjangoModelPermissionsOrAnonReadOnly]
+ queryset = Book.objects.none() # Required for DjangoModelPermissions
+ serializer_class = serializers.BookListSerializer
+
+ def get(self, request, filename=None, **kwargs):
+ if filename and not kwargs.get('tags') and 'count' not in request.query_params:
+ try:
+ with open(os.path.join(settings.MEDIA_ROOT, 'api', '%s.%s' % (filename, request.accepted_renderer.format)), 'rb') as f:
+ content = f.read()
+ return HttpResponse(content, content_type=request.accepted_media_type)
+ except:
+ pass
+ return super().get(request, filename=filename, **kwargs)
+
+ def get_queryset(self):
+ try:
+ tags, ancestors = read_tags(
+ self.kwargs.get('tags', ''), self.request,
+ allowed=('author', 'epoch', 'kind', 'genre')
+ )
+ except ValueError:
+ raise Http404
+
+ new_api = self.request.query_params.get('new_api')
+ after = self.request.query_params.get('after', self.kwargs.get('after'))
+ count = self.request.query_params.get('count', self.kwargs.get('count'))
+ if count:
+ try:
+ count = int(count)
+ except TypeError:
+ raise Http404 # Fixme
+
+ if tags:
+ if self.kwargs.get('top_level'):
+ books = Book.tagged_top_level(tags)
+ if not books:
+ raise Http404
+ return books
+ else:
+ books = Book.tagged.with_all(tags)
+ else:
+ books = Book.objects.all()
+ books = books.filter(findable=True)
+ books = order_books(books, new_api)
+
+ if not Membership.is_active_for(self.request.user):
+ books = books.exclude(preview=True)
+
+ if self.kwargs.get('top_level'):
+ books = books.filter(parent=None)
+ if self.kwargs.get('audiobooks'):
+ books = books.filter(media__type='mp3').distinct()
+ if self.kwargs.get('daisy'):
+ books = books.filter(media__type='daisy').distinct()
+ if self.kwargs.get('recommended'):
+ books = books.filter(recommended=True)
+ if self.kwargs.get('newest'):
+ books = books.order_by('-created_at')
+
+ if after:
+ books = books_after(books, after, new_api)
+
+ prefetch_relations(books, 'author')
+ prefetch_relations(books, 'genre')
+ prefetch_relations(books, 'kind')
+ prefetch_relations(books, 'epoch')
+
+ if count:
+ books = books[:count]
+
+ return books
+
+ def post(self, request, **kwargs):
+ if kwargs.get('audiobooks'):
+ return self.post_audiobook(request, **kwargs)
+ else:
+ return self.post_book(request, **kwargs)
+
+ def post_book(self, request, **kwargs):
+ data = json.loads(request.POST.get('data'))
+ form = BookImportForm(data)
+ if form.is_valid():
+ form.save()
+ return Response({}, status=status.HTTP_201_CREATED)
+ else:
+ raise Http404
+
+ def post_audiobook(self, request, **kwargs):
+ index = int(request.POST['part_index'])
+ parts_count = int(request.POST['parts_count'])
+ media_type = request.POST['type'].lower()
+ source_sha1 = request.POST.get('source_sha1')
+ name = request.POST.get('name', '')
+ part_name = request.POST.get('part_name', '')
+
+ project_data = request.POST.get('project', {})
+ project_description = project_data.get('description', '')
+ project_icon = project_data.get('icon', '')
+
+ _rest, slug = request.POST['book'].rstrip('/').rsplit('/', 1)
+ book = Book.objects.get(slug=slug)
+
+ try:
+ assert source_sha1
+ bm = book.media.get(type=media_type, source_sha1=source_sha1)
+ except (AssertionError, BookMedia.DoesNotExist):
+ bm = BookMedia(book=book, type=media_type)
+ bm.name = name
+ bm.part_name = part_name
+ bm.index = index
+ bm.project_description = project_description
+ bm.project_icon = project_icon
+ bm.file.save(None, request.data['file'], save=False)
+ bm.save(parts_count=parts_count)
+
+ return Response({}, status=status.HTTP_201_CREATED)
+
+
+@vary_on_auth # Because of 'liked'.