-# This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
-# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+# This file is part of Wolne Lektury, licensed under GNU Affero GPLv3 or later.
+# Copyright © Fundacja Wolne Lektury. See NOTICE for more information.
#
+import io
import os
+import pkg_resources
+import random
+import time
+from urllib.request import urlopen
+from django.apps import apps
from django.conf import settings
from django.core.files import File
from django.db import models
from django.db.models.fields.files import FieldFile
from django.utils.deconstruct import deconstructible
-from django.utils.translation import gettext_lazy as _
-from catalogue.constants import LANGUAGES_3TO2, EBOOK_FORMATS_WITH_CHILDREN, EBOOK_FORMATS_WITHOUT_CHILDREN
+from librarian.cover import make_cover
+from catalogue.constants import LANGUAGES_3TO2
from catalogue.utils import absolute_url, remove_zip, truncate_html_words, gallery_path, gallery_url
from waiter.utils import clear_cache
return isinstance(other, type(self)) and other.path == self.path
+def get_make_cover(book):
+ extra = book.get_extra_info_json()
+ cover_logo = extra.get('logo_mono', extra.get('logo'))
+ if cover_logo:
+ while True:
+ try:
+ cover_logo = io.BytesIO(urlopen(cover_logo, timeout=3).read())
+ except:
+ time.sleep(2)
+ else:
+ break
+
+ def mc(*args, **kwargs):
+ if cover_logo:
+ kwargs['cover_logo'] = cover_logo
+ return make_cover(*args, **kwargs)
+ return mc
+
+
class EbookFieldFile(FieldFile):
"""Represents contents of an ebook file field."""
"""Represents an ebook file field, attachable to a model."""
attr_class = EbookFieldFile
ext = None
+ for_parents = True
librarian2_api = False
ZIP = None
setattr(cls, 'has_%s' % self.attname, has)
def get_current_etag(self):
- import pkg_resources
+ MediaInsertSet = apps.get_model('annoy', 'MediaInsertSet')
librarian_version = pkg_resources.get_distribution("librarian").version
- return librarian_version
-
- def schedule_stale(self, queryset=None):
- """Schedule building this format for all the books where etag is stale."""
+ etag = librarian_version
+ mis = MediaInsertSet.get_for_format(self.ext)
+ if mis is not None:
+ etag += '_' + mis.etag
+ return etag
+
+ def find_stale(self, limit):
+ """Find some books where this format is stale."""
# If there is not ETag field, bail. That's true for xml file field.
if not self.with_etag:
- return
+ return []
etag = self.get_current_etag()
- if queryset is None:
- queryset = self.model.objects.all()
- if self.format_name in EBOOK_FORMATS_WITHOUT_CHILDREN + ['html']:
+ queryset = self.model.objects.all()
+ if not self.for_parents:
queryset = queryset.filter(children=None)
queryset = queryset.exclude(**{
f'{self.etag_field_name}__in': [
etag, f'{etag}{ETAG_SCHEDULED_SUFFIX}'
- ]
+ ]
})
- for obj in queryset:
- fieldfile = getattr(obj, self.attname)
- priority = EBOOK_REBUILD_PRIORITY if fieldfile else EBOOK_BUILD_PRIORITY
- fieldfile.build_delay(priority=priority)
+
+ queryset = queryset.order_by('?')[:limit]
+ return queryset
@classmethod
- def schedule_all_stale(cls, model):
+ def find_all_stale(cls, model, limit):
"""Schedules all stale ebooks of all formats to rebuild."""
+ found = []
for field in model._meta.fields:
if isinstance(field, cls):
- field.schedule_stale()
+ for instance in field.find_stale(limit):
+ found.append((
+ field.name,
+ instance
+ ))
+ random.shuffle(found)
+ found = found[:limit]
+ return found
@staticmethod
- def transform(wldoc):
+ def transform(wldoc, book):
"""Transforms an librarian.WLDocument into an librarian.OutputFile.
"""
raise NotImplemented()
book = fieldfile.instance
out = self.transform(
book.wldocument2() if self.librarian2_api else book.wldocument(),
+ book,
)
- fieldfile.save(None, File(open(out.get_filename(), 'rb')), save=False)
+ with open(out.get_filename(), 'rb') as f:
+ fieldfile.save(None, File(f), save=False)
self.set_file_permissions(fieldfile)
if book.pk is not None:
book.save(update_fields=[self.attname])
class TxtField(EbookField):
ext = 'txt'
+ for_parents = False
@staticmethod
- def transform(wldoc):
+ def transform(wldoc, book):
return wldoc.as_text()
class Fb2Field(EbookField):
ext = 'fb2'
+ for_parents = False
ZIP = 'wolnelektury_pl_fb2'
@staticmethod
- def transform(wldoc):
+ def transform(wldoc, book):
return wldoc.as_fb2()
ZIP = 'wolnelektury_pl_pdf'
@staticmethod
- def transform(wldoc):
+ def transform(wldoc, book):
return wldoc.as_pdf(
- morefloats=settings.LIBRARIAN_PDF_MOREFLOATS, cover=True,
+ morefloats=settings.LIBRARIAN_PDF_MOREFLOATS,
+ cover=get_make_cover(book),
base_url=absolute_url(gallery_url(wldoc.book_info.url.slug)), customizations=['notoc'])
def build(self, fieldfile):
ZIP = 'wolnelektury_pl_epub'
@staticmethod
- def transform(wldoc):
+ def transform(wldoc, book):
from librarian.builders import EpubBuilder
+ MediaInsertSet = apps.get_model('annoy', 'MediaInsertSet')
return EpubBuilder(
base_url='file://' + os.path.abspath(gallery_path(wldoc.meta.url.slug)) + '/',
- fundraising=settings.EPUB_FUNDRAISING
+ fundraising=MediaInsertSet.get_texts_for('epub'),
+ cover=get_make_cover(book),
).build(wldoc)
ZIP = 'wolnelektury_pl_mobi'
@staticmethod
- def transform(wldoc):
+ def transform(wldoc, book):
from librarian.builders import MobiBuilder
+ MediaInsertSet = apps.get_model('annoy', 'MediaInsertSet')
return MobiBuilder(
base_url='file://' + os.path.abspath(gallery_path(wldoc.meta.url.slug)) + '/',
- fundraising=settings.EPUB_FUNDRAISING
+ fundraising=MediaInsertSet.get_texts_for('mobi'),
+ cover=get_make_cover(book),
).build(wldoc)
class HtmlField(EbookField):
ext = 'html'
+ for_parents = False
def build(self, fieldfile):
from django.core.files.base import ContentFile
book = fieldfile.instance
- html_output = self.transform(book.wldocument(parse_dublincore=False))
+ html_output = self.transform(book.wldocument(parse_dublincore=False), book)
# Delete old fragments, create from scratch if necessary.
book.fragments.all().delete()
return False
@staticmethod
- def transform(wldoc):
+ def transform(wldoc, book):
# ugly, but we can't use wldoc.book_info here
from librarian import DCNS
url_elem = wldoc.edoc.getroot().find('.//' + DCNS('identifier.url'))
directory = 'cover'
@staticmethod
- def transform(wldoc):
- return wldoc.as_cover()
+ def transform(wldoc, book):
+ return get_make_cover(book)(wldoc.book_info, width=360).output_file()
def set_file_permissions(self, fieldfile):
pass
directory = 'cover_clean'
@staticmethod
- def transform(wldoc):
- if wldoc.book_info.cover_box_position == 'none':
- from librarian.cover import WLCover
- return WLCover(wldoc.book_info, width=240).output_file()
- from librarian.covers.marquise import MarquiseCover
- return MarquiseCover(wldoc.book_info, width=240).output_file()
+ def transform(wldoc, book):
+ return get_make_cover(book)(wldoc.book_info, width=360).output_file()
class CoverThumbField(CoverField):
directory = 'cover_thumb'
@staticmethod
- def transform(wldoc):
+ def transform(wldoc, book):
from librarian.cover import WLCover
return WLCover(wldoc.book_info, height=193).output_file()
directory = 'cover_api_thumb'
@staticmethod
- def transform(wldoc):
+ def transform(wldoc, book):
from librarian.cover import WLNoBoxCover
return WLNoBoxCover(wldoc.book_info, height=500).output_file()
directory = 'cover_simple'
@staticmethod
- def transform(wldoc):
+ def transform(wldoc, book):
from librarian.cover import WLNoBoxCover
return WLNoBoxCover(wldoc.book_info, height=1000).output_file()
directory = 'cover_ebookpoint'
@staticmethod
- def transform(wldoc):
+ def transform(wldoc, book):
from librarian.cover import EbookpointCover
return EbookpointCover(wldoc.book_info).output_file()