fix for bookmarks
[wolnelektury.git] / src / catalogue / views.py
index 3c4a55f..4873106 100644 (file)
@@ -5,11 +5,13 @@ from collections import OrderedDict
 import random
 import re
 from urllib.parse import quote_plus
+from slugify import slugify
+from zipstream import ZipStream
 
 from django.conf import settings
 from django.template.loader import render_to_string
 from django.shortcuts import get_object_or_404, render, redirect
-from django.http import HttpResponse, HttpResponseRedirect, Http404, HttpResponsePermanentRedirect
+from django.http import HttpResponse, HttpResponseRedirect, Http404, HttpResponsePermanentRedirect, StreamingHttpResponse
 from django.urls import reverse
 from django.db.models import Q, QuerySet
 from django.contrib.auth.decorators import login_required, user_passes_test
@@ -23,6 +25,7 @@ from club.forms import DonationStep1Form
 from club.models import Club
 from annoy.models import DynamicTextInsert
 from pdcounter import views as pdcounter_views
+from social.models import UserList
 from wolnelektury.utils import is_ajax
 from catalogue import constants
 from catalogue import forms
@@ -211,25 +214,45 @@ class AudiobooksView(LiteratureView):
 class TaggedObjectList(BookList):
     def analyse(self):
         super().analyse()
+
         self.ctx['tags'] = analyse_tags(self.request, self.kwargs['tags'])
-        self.ctx['fragment_tags'] = [t for t in self.ctx['tags'] if t.category in ('theme', 'object')]
-        self.ctx['work_tags'] = [t for t in self.ctx['tags'] if t not in self.ctx['fragment_tags']]
+        if len(self.ctx['tags']) > 4:
+            raise Http404
+        self.ctx.update({
+            'fragment_tags': [],
+            'work_tags': [],
+            'user_lists': [],
+        })
+        for tag in self.ctx['tags']:
+            if isinstance(tag, UserList):
+                self.ctx['user_lists'].append(tag)
+            elif tag.category == 'theme':
+                self.ctx['fragment_tags'].append(tag)
+            else:
+                self.ctx['work_tags'].append(tag)
+
         self.is_themed = self.ctx['has_theme'] = bool(self.ctx['fragment_tags'])
         if self.is_themed:
             self.ctx['main_tag'] = self.ctx['fragment_tags'][0]
-        elif self.ctx['tags']:
-            self.ctx['main_tag'] = self.ctx['tags'][0]
+        elif self.ctx['work_tags']:
+            self.ctx['main_tag'] = self.ctx['work_tags'][0]
         else:
             self.ctx['main_tag'] = None
         self.ctx['filtering_tags'] = [
             t for t in self.ctx['tags']
             if t is not self.ctx['main_tag']
         ]
-        if len(self.ctx['tags']) == 1 and self.ctx['main_tag'].category == 'author':
+        if len(self.ctx['tags']) == 1 and self.ctx['main_tag'] is not None and self.ctx['main_tag'].category == 'author':
             self.ctx['translation_list'] = self.ctx['main_tag'].book_set.all()
+            self.ctx['narrated'] = self.ctx['main_tag'].narrated.all()
 
     def get_queryset(self):
-        qs = Book.tagged.with_all(self.ctx['work_tags']).filter(findable=True)
+        if self.ctx['work_tags']:
+            qs = Book.tagged.with_all(self.ctx['work_tags']).filter(findable=True)
+        else:
+            qs = Book.objects.filter(findable=True)
+        for ul in self.ctx['user_lists']:
+            qs = qs.filter(id__in=[i.id for i in ul.get_books()])
         qs = qs.exclude(ancestor__in=qs)
         if self.is_themed:
             fqs = Fragment.tagged.with_all(self.ctx['fragment_tags'])
@@ -241,6 +264,9 @@ class TaggedObjectList(BookList):
         return qs
 
     def get_suggested_tags(self, queryset):
+        if self.ctx['user_lists']:
+            # TODO
+            return []
         tag_ids = [t.id for t in self.ctx['tags']]
         if self.is_themed:
             related_tags = []
@@ -252,6 +278,7 @@ class TaggedObjectList(BookList):
                     containing_books,
                 ).exclude(category='set').exclude(pk__in=tag_ids)
             ))
+            ### FIXME: These won't be tags
             if self.request.user.is_authenticated:
                 related_tags.extend(list(
                     Tag.objects.usage_for_queryset(
@@ -292,6 +319,7 @@ def object_list(request, objects, list_type='books'):
             Tag.objects.usage_for_queryset(
                 objects, counts=True
             ).exclude(category='set'))
+        ### FIXME: these won't be tags
         if request.user.is_authenticated:
             related_tag_lists.append(
                 Tag.objects.usage_for_queryset(
@@ -309,7 +337,7 @@ def object_list(request, objects, list_type='books'):
             .only('name', 'sort_key', 'category', 'slug'))
         if isinstance(objects, QuerySet):
             objects = prefetch_relations(objects, 'author')
-    
+
     categories = split_tags(*related_tag_lists)
     suggest = []
     for c in ['set', 'author', 'epoch', 'kind', 'genre']:
@@ -324,7 +352,7 @@ def object_list(request, objects, list_type='books'):
     }
 
     template = 'catalogue/author_detail.html'
-        
+
     return render(
         request, template, result,
     )
@@ -492,6 +520,31 @@ def download_zip(request, file_format=None, media_format=None, slug=None):
     return HttpResponseRedirect(quote_plus(settings.MEDIA_URL + url, safe='/?='))
 
 
+def stream_zip(request, media_format=None, slug=None):
+    book = get_object_or_404(Book, slug=slug)
+    def iterate_audiobooks(book, names):
+        for bm in book.media.filter(type=media_format).order_by('index'):
+            yield (
+                bm.file.path,
+                names + (slugify(bm.part_name),) if bm.part_name else names
+            )
+        for child in book.get_children():
+            yield from iterate_audiobooks(child, names + (slugify(child.title),))
+
+    zs = ZipStream()
+
+    for i, (file_path, names) in enumerate(iterate_audiobooks(book, ())):
+        index = i + 1
+        part_name = '_'.join(names)
+        ext = file_path.rsplit('.', 1)[-1]
+        zip_name = f'{book.slug}_{index:03d}_{part_name}'[:240] + '.' + ext
+        zs.add_path(file_path, zip_name)
+
+    response = StreamingHttpResponse(zs, content_type='application/zip')
+    response['Content-Disposition'] = f'attachment; filename={slug}_{media_format}.zip'
+    return response
+
+
 class CustomPDFFormView(AjaxableFormView):
     form_class = forms.CustomPDFForm
     title = gettext_lazy('Stwórz własny PDF')