X-Git-Url: https://git.mdrn.pl/wolnelektury.git/blobdiff_plain/18aa8ca52202003e5628a882f3469a04d905cc05..eebe542f3d49cdce6d851587ddf8bd2cb1bd8ef1:/src/catalogue/models/book.py diff --git a/src/catalogue/models/book.py b/src/catalogue/models/book.py index 411476aa0..ff09853f5 100644 --- a/src/catalogue/models/book.py +++ b/src/catalogue/models/book.py @@ -43,6 +43,7 @@ class Book(models.Model): common_slug = models.SlugField('wspólny slug', max_length=120, db_index=True) language = models.CharField('kod języka', max_length=3, db_index=True, default=app_settings.DEFAULT_LANGUAGE) description = models.TextField('opis', blank=True) + license = models.CharField('licencja', max_length=255, blank=True, db_index=True) abstract = models.TextField('abstrakt', blank=True) toc = models.TextField('spis treści', blank=True) created_at = models.DateTimeField('data utworzenia', auto_now_add=True, db_index=True) @@ -62,6 +63,7 @@ class Book(models.Model): # files generated during publication xml_file = fields.XmlField(storage=bofh_storage, with_etag=False) html_file = fields.HtmlField(storage=bofh_storage) + html_nonotes_file = fields.HtmlNonotesField(storage=bofh_storage) fb2_file = fields.Fb2Field(storage=bofh_storage) txt_file = fields.TxtField(storage=bofh_storage) epub_file = fields.EpubField(storage=bofh_storage) @@ -79,7 +81,7 @@ class Book(models.Model): 'okładka dla Ebookpoint') ebook_formats = constants.EBOOK_FORMATS - formats = ebook_formats + ['html', 'xml'] + formats = ebook_formats + ['html', 'xml', 'html_nonotes'] parent = models.ForeignKey('self', models.CASCADE, blank=True, null=True, related_name='children') ancestor = models.ManyToManyField('self', blank=True, editable=False, related_name='descendant', symmetrical=False) @@ -91,6 +93,7 @@ class Book(models.Model): tagged = managers.ModelTaggedItemManager(Tag) tags = managers.TagDescriptor(Tag) tag_relations = GenericRelation(Tag.intermediary_table_model) + translators = models.ManyToManyField(Tag, blank=True) html_built = django.dispatch.Signal() published = django.dispatch.Signal() @@ -154,12 +157,6 @@ class Book(models.Model): def genre_unicode(self): return self.tag_unicode('genre') - def translators(self): - translators = self.get_extra_info_json().get('translators') or [] - return [ - '\xa0'.join(reversed(translator.split(', ', 1))) for translator in translators - ] - def translator(self): translators = self.get_extra_info_json().get('translators') if not translators: @@ -331,6 +328,9 @@ class Book(models.Model): total += app_settings.GET_MP3_LENGTH(media.file.path) return int(total) + def get_time(self): + return round(self.xml_file.size / 1000 * 40) + def has_media(self, type_): if type_ in Book.formats: return bool(getattr(self, "%s_file" % type_)) @@ -377,6 +377,9 @@ class Book(models.Model): def html_url(self): return self.media_url('html') + def html_nonotes_url(self): + return self.media_url('html_nonotes') + def pdf_url(self): return self.media_url('pdf') @@ -416,8 +419,69 @@ class Book(models.Model): has_daisy_file.boolean = True def has_sync_file(self): - return self.has_media("sync") + return settings.FEATURE_SYNCHRO and self.has_media("sync") + + def build_sync_file(self): + from lxml import html + from django.core.files.base import ContentFile + with self.html_file.open('rb') as f: + h = html.fragment_fromstring(f.read().decode('utf-8')) + + durations = [ + m['mp3'].duration + for m in self.get_audiobooks()[0] + ] + if settings.MOCK_DURATIONS: + durations = settings.MOCK_DURATIONS + + sync = [] + ts = None + sid = 1 + dirty = False + for elem in h.iter(): + if elem.get('data-audio-ts'): + part, ts = int(elem.get('data-audio-part')), float(elem.get('data-audio-ts')) + ts = str(round(sum(durations[:part - 1]) + ts, 3)) + # check if inside verse + p = elem.getparent() + while p is not None: + # Workaround for missing ids. + if 'verse' in p.get('class', ''): + if not p.get('id'): + p.set('id', f'syn{sid}') + dirty = True + sid += 1 + sync.append((ts, p.get('id'))) + ts = None + break + p = p.getparent() + elif ts: + cls = elem.get('class', '') + # Workaround for missing ids. + if 'paragraph' in cls or 'verse' in cls or elem.tag in ('h1', 'h2', 'h3', 'h4'): + if not elem.get('id'): + elem.set('id', f'syn{sid}') + dirty = True + sid += 1 + sync.append((ts, elem.get('id'))) + ts = None + if dirty: + htext = html.tostring(h, encoding='utf-8') + with open(self.html_file.path, 'wb') as f: + f.write(htext) + try: + bm = self.media.get(type='sync') + except: + bm = BookMedia(book=self, type='sync') + sync = ( + '27\n' + '\n'.join( + f'{s[0]}\t{sync[i+1][0]}\t{s[1]}' for i, s in enumerate(sync[:-1]) + )).encode('latin1') + bm.file.save( + None, ContentFile(sync) + ) + def get_sync(self): with self.get_media('sync').first().file.open('r') as f: sync = f.read().split('\n') @@ -594,7 +658,7 @@ class Book(models.Model): @classmethod def from_text_and_meta(cls, raw_file, book_info, overwrite=False, dont_build=None, search_index=True, - remote_gallery_url=None, days=0, findable=True): + remote_gallery_url=None, days=0, findable=True, logo=None, logo_mono=None, logo_alt=None): from catalogue import tasks if dont_build is None: @@ -637,25 +701,31 @@ class Book(models.Model): book.findable = findable book.language = book_info.language book.title = book_info.title + book.license = book_info.license or '' if book_info.variant_of: book.common_slug = book_info.variant_of.slug else: book.common_slug = book.slug - book.extra_info = json.dumps(book_info.to_dict()) + extra = book_info.to_dict() + if logo: + extra['logo'] = logo + if logo_mono: + extra['logo_mono'] = logo_mono + if logo_alt: + extra['logo_alt'] = logo_alt + book.extra_info = json.dumps(extra) book.load_abstract() book.load_toc() book.save() meta_tags = Tag.tags_from_info(book_info) - for tag in meta_tags: - if not tag.for_books: - tag.for_books = True - tag.save() - - book.tags = set(meta_tags + book_shelves) + just_tags = [t for (t, rel) in meta_tags if not rel] + book.tags = set(just_tags + book_shelves) book.save() # update sort_key_author + book.translators.set([t for (t, rel) in meta_tags if rel == 'translator']) + cover_changed = old_cover != book.cover_info() obsolete_children = set(b for b in book.children.all() if b not in children) @@ -701,6 +771,7 @@ class Book(models.Model): for format_ in constants.EBOOK_FORMATS_WITH_CHILDREN: if format_ not in dont_build: getattr(book, '%s_file' % format_).build_delay() + book.html_nonotes_file.build_delay() if not settings.NO_SEARCH_INDEX and search_index and findable: tasks.index_book.delay(book.id) @@ -729,8 +800,12 @@ class Book(models.Model): else: entity, entity_created = Entity.objects.get_or_create(uri=uri) if entity_created: - entity.populate() - entity.save() + try: + entity.populate() + except: + pass + else: + entity.save() ref, ref_created = entity.reference_set.get_or_create(book=self) refs[uri] = ref if not ref_created: