+@login_required
+@require_POST
+@cache.never_cache
+def remove_from_shelf(request, shelf, book):
+ book = get_object_or_404(models.Book, slug=book)
+ shelf = get_object_or_404(models.Tag, slug=shelf, category='set', user=request.user)
+
+ if shelf in book.tags:
+ models.Tag.objects.remove_tag(book, shelf)
+
+ shelf.book_count = None
+ shelf.save()
+
+ return HttpResponse(_('Book was successfully removed from the shelf'))
+ else:
+ return HttpResponse(_('This book is not on the shelf'))
+
+
+def collect_books(books):
+ """
+ Returns all real books in collection.
+ """
+ result = []
+ for book in books:
+ if len(book.children.all()) == 0:
+ result.append(book)
+ else:
+ result += collect_books(book.children.all())
+ return result
+
+
+@cache.never_cache
+def download_shelf(request, slug):
+ """"
+ Create a ZIP archive on disk and transmit it in chunks of 8KB,
+ without loading the whole file into memory. A similar approach can
+ be used for large dynamic PDF files.
+ """
+ shelf = get_object_or_404(models.Tag, slug=slug, category='set')
+
+ formats = []
+ form = forms.DownloadFormatsForm(request.GET)
+ if form.is_valid():
+ formats = form.cleaned_data['formats']
+ if len(formats) == 0:
+ formats = ['pdf', 'epub', 'odt', 'txt', 'mp3', 'ogg', 'daisy']
+
+ # Create a ZIP archive
+ temp = tempfile.TemporaryFile()
+ archive = zipfile.ZipFile(temp, 'w')
+
+ already = set()
+ for book in collect_books(models.Book.tagged.with_all(shelf)):
+ if 'pdf' in formats and book.pdf_file:
+ filename = book.pdf_file.path
+ archive.write(filename, str('%s.pdf' % book.slug))
+ if book.root_ancestor not in already and 'epub' in formats and book.root_ancestor.epub_file:
+ filename = book.root_ancestor.epub_file.path
+ archive.write(filename, str('%s.epub' % book.root_ancestor.slug))
+ already.add(book.root_ancestor)
+ if 'odt' in formats and book.has_media("odt"):
+ for file in book.get_media("odt"):
+ filename = file.file.path
+ archive.write(filename, str('%s.odt' % slugify(file.name)))
+ if 'txt' in formats and book.txt_file:
+ filename = book.txt_file.path
+ archive.write(filename, str('%s.txt' % book.slug))
+ if 'mp3' in formats and book.has_media("mp3"):
+ for file in book.get_media("mp3"):
+ filename = file.file.path
+ archive.write(filename, str('%s.mp3' % slugify(file.name)))
+ if 'ogg' in formats and book.has_media("ogg"):
+ for file in book.get_media("ogg"):
+ filename = file.file.path
+ archive.write(filename, str('%s.ogg' % slugify(file.name)))
+ if 'daisy' in formats and book.has_media("daisy"):
+ for file in book.get_media("daisy"):
+ filename = file.file.path
+ archive.write(filename, str('%s.daisy' % slugify(file.name)))
+ archive.close()
+
+ response = HttpResponse(content_type='application/zip', mimetype='application/x-zip-compressed')
+ response['Content-Disposition'] = 'attachment; filename=%s.zip' % slughifi(shelf.name)
+ response['Content-Length'] = temp.tell()
+
+ temp.seek(0)
+ response.write(temp.read())
+ return response
+
+
+@cache.never_cache
+def shelf_book_formats(request, shelf):
+ """"
+ Returns a list of formats of books in shelf.
+ """
+ shelf = get_object_or_404(models.Tag, slug=shelf, category='set')
+
+ formats = {'pdf': False, 'epub': False, 'odt': False, 'txt': False, 'mp3': False, 'ogg': False, 'daisy': False}
+
+ for book in collect_books(models.Book.tagged.with_all(shelf)):
+ if book.pdf_file:
+ formats['pdf'] = True
+ if book.root_ancestor.epub_file:
+ formats['epub'] = True
+ if book.odt_file:
+ formats['odt'] = True
+ if book.txt_file:
+ formats['txt'] = True
+ if book.mp3_file:
+ formats['mp3'] = True
+ if book.ogg_file:
+ formats['ogg'] = True
+ if book.daisy_file:
+ formats['daisy'] = True
+
+ return HttpResponse(LazyEncoder().encode(formats))
+
+