From: Radek Czajka Date: Thu, 27 Oct 2011 13:54:29 +0000 (+0200) Subject: initial mobi format support (with a really awful icon) X-Git-Url: https://git.mdrn.pl/wolnelektury.git/commitdiff_plain/6e75a30246598a40cabb8ca1498c3a4f6f146c91?ds=sidebyside;hp=-c initial mobi format support (with a really awful icon) some format generalizations minor fixes --- 6e75a30246598a40cabb8ca1498c3a4f6f146c91 diff --git a/apps/api/handlers.py b/apps/api/handlers.py index 41b5ac6f9..8f06deafc 100644 --- a/apps/api/handlers.py +++ b/apps/api/handlers.py @@ -93,8 +93,7 @@ class BookDetailHandler(BaseHandler): """ allowed_methods = ['GET'] - fields = ['title', 'parent', - 'xml', 'html', 'pdf', 'epub', 'txt', + fields = ['title', 'parent'] + Book.file_types + [ 'media', 'url'] + category_singular.keys() @piwik_track @@ -203,7 +202,7 @@ def _file_getter(format): else: return '' return get_file -for format in ('xml', 'txt', 'html', 'epub', 'pdf'): +for format in Book.file_types: setattr(BooksHandler, format, _file_getter(format)) @@ -353,16 +352,16 @@ class CatalogueHandler(BaseHandler): @staticmethod def book_dict(book, fields=None): - all_fields = ('url', 'title', 'description', + all_fields = ['url', 'title', 'description', 'gazeta_link', 'wiki_link', - 'xml', 'epub', 'txt', 'pdf', 'html', + ] + Book.file_types + [ 'mp3', 'ogg', 'daisy', 'parent', 'parent_number', 'tags', 'license', 'license_description', 'source_name', 'technical_editors', 'editors', 'author', 'sort_key', - ) + ] if fields: fields = (f for f in fields if f in all_fields) else: @@ -373,7 +372,7 @@ class CatalogueHandler(BaseHandler): obj = {} for field in fields: - if field in ('xml', 'epub', 'txt', 'pdf', 'html'): + if field in Book.file_types: f = getattr(book, field+'_file') if f: obj[field] = { diff --git a/apps/catalogue/forms.py b/apps/catalogue/forms.py index 2bf974d44..04969c2e2 100644 --- a/apps/catalogue/forms.py +++ b/apps/catalogue/forms.py @@ -85,6 +85,7 @@ FORMATS = ( ('txt', 'TXT'), ('epub', 'EPUB'), ('daisy', 'DAISY'), + ('mobi', 'MOBI'), ) diff --git a/apps/catalogue/management/commands/importbooks.py b/apps/catalogue/management/commands/importbooks.py index f51214d72..ecd3fcc97 100644 --- a/apps/catalogue/management/commands/importbooks.py +++ b/apps/catalogue/management/commands/importbooks.py @@ -22,6 +22,8 @@ class Command(BaseCommand): help='Print status messages to stdout'), make_option('-E', '--no-build-epub', action='store_false', dest='build_epub', default=True, help='Don\'t build EPUB file'), + make_option('-M', '--no-build-mobi', action='store_false', dest='build_mobi', default=True, + help='Don\'t build MOBI file'), make_option('-T', '--no-build-txt', action='store_false', dest='build_txt', default=True, help='Don\'t build TXT file'), make_option('-P', '--no-build-pdf', action='store_false', dest='build_pdf', default=True, @@ -84,13 +86,18 @@ class Command(BaseCommand): book = Book.from_xml_file(file_path, overwrite=force, build_epub=options.get('build_epub'), build_txt=options.get('build_txt'), - build_pdf=options.get('build_pdf')) + build_pdf=options.get('build_pdf'), + build_mobi=options.get('build_mobi')) files_imported += 1 if os.path.isfile(file_base + '.pdf'): book.pdf_file.save('%s.pdf' % book.slug, File(file(file_base + '.pdf'))) if verbose: print "Importing %s.pdf" % file_base + if os.path.isfile(file_base + '.mobi'): + book.mobi_file.save('%s.mobi' % book.slug, File(file(file_base + '.mobi'))) + if verbose: + print "Importing %s.mobi" % file_base if os.path.isfile(file_base + '.epub'): book.epub_file.save('%s.epub' % book.slug, File(file(file_base + '.epub'))) if verbose: diff --git a/apps/catalogue/management/commands/pack.py b/apps/catalogue/management/commands/pack.py index a300aff0f..c75f092a9 100755 --- a/apps/catalogue/management/commands/pack.py +++ b/apps/catalogue/management/commands/pack.py @@ -24,7 +24,6 @@ class Command(BaseCommand): help='Exclude specific books by slug') ) help = 'Prepare data for Lesmianator.' - ftypes = ['xml', 'txt', 'html', 'epub', 'pdf'] args = '[%s] output_path.zip' % '|'.join(ftypes) def handle(self, ftype, path, **options): @@ -34,7 +33,7 @@ class Command(BaseCommand): include = options.get('include') exclude = options.get('exclude') - if ftype in self.ftypes: + if ftype in Book.file_types: field = "%s_file" % ftype else: print self.style.ERROR('Unknown file type.') diff --git a/apps/catalogue/migrations/0016_auto__add_field_book_mobi_file.py b/apps/catalogue/migrations/0016_auto__add_field_book_mobi_file.py new file mode 100644 index 000000000..87faf6bc2 --- /dev/null +++ b/apps/catalogue/migrations/0016_auto__add_field_book_mobi_file.py @@ -0,0 +1,131 @@ +# encoding: utf-8 +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + +class Migration(SchemaMigration): + + def forwards(self, orm): + + # Adding field 'Book.mobi_file' + db.add_column('catalogue_book', 'mobi_file', self.gf('django.db.models.fields.files.FileField')(default='', max_length=100, blank=True), keep_default=False) + + + def backwards(self, orm): + + # Deleting field 'Book.mobi_file' + db.delete_column('catalogue_book', 'mobi_file') + + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'catalogue.book': { + 'Meta': {'ordering': "('sort_key',)", 'object_name': 'Book'}, + 'changed_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'db_index': 'True', 'blank': 'True'}), + 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'db_index': 'True', 'blank': 'True'}), + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'epub_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'blank': 'True'}), + 'extra_info': ('catalogue.fields.JSONField', [], {'default': "'{}'"}), + 'gazeta_link': ('django.db.models.fields.CharField', [], {'max_length': '240', 'blank': 'True'}), + 'html_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'mobi_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'blank': 'True'}), + 'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['catalogue.Book']"}), + 'parent_number': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'pdf_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'blank': 'True'}), + 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '120', 'db_index': 'True'}), + 'sort_key': ('django.db.models.fields.CharField', [], {'max_length': '120', 'db_index': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '120'}), + 'txt_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'blank': 'True'}), + 'wiki_link': ('django.db.models.fields.CharField', [], {'max_length': '240', 'blank': 'True'}), + 'xml_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'blank': 'True'}) + }, + 'catalogue.bookmedia': { + 'Meta': {'ordering': "('type', 'name')", 'object_name': 'BookMedia'}, + 'book': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'media'", 'to': "orm['catalogue.Book']"}), + 'extra_info': ('catalogue.fields.JSONField', [], {'default': "'{}'"}), + 'file': ('catalogue.fields.OverwritingFileField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': "'100'"}), + 'source_sha1': ('django.db.models.fields.CharField', [], {'max_length': '40', 'null': 'True', 'blank': 'True'}), + 'type': ('django.db.models.fields.CharField', [], {'max_length': "'100'"}), + 'uploaded_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}) + }, + 'catalogue.filerecord': { + 'Meta': {'ordering': "('-time', '-slug', '-type')", 'object_name': 'FileRecord'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'sha1': ('django.db.models.fields.CharField', [], {'max_length': '40'}), + 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '120', 'db_index': 'True'}), + 'time': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'type': ('django.db.models.fields.CharField', [], {'max_length': '20', 'db_index': 'True'}) + }, + 'catalogue.fragment': { + 'Meta': {'ordering': "('book', 'anchor')", 'object_name': 'Fragment'}, + 'anchor': ('django.db.models.fields.CharField', [], {'max_length': '120'}), + 'book': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'fragments'", 'to': "orm['catalogue.Book']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'short_text': ('django.db.models.fields.TextField', [], {}), + 'text': ('django.db.models.fields.TextField', [], {}) + }, + 'catalogue.tag': { + 'Meta': {'ordering': "('sort_key',)", 'unique_together': "(('slug', 'category'),)", 'object_name': 'Tag'}, + 'book_count': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'category': ('django.db.models.fields.CharField', [], {'max_length': '50', 'db_index': 'True'}), + 'changed_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'db_index': 'True', 'blank': 'True'}), + 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'db_index': 'True', 'blank': 'True'}), + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'gazeta_link': ('django.db.models.fields.CharField', [], {'max_length': '240', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'main_page': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50', 'db_index': 'True'}), + 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '120', 'db_index': 'True'}), + 'sort_key': ('django.db.models.fields.CharField', [], {'max_length': '120', 'db_index': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}), + 'wiki_link': ('django.db.models.fields.CharField', [], {'max_length': '240', 'blank': 'True'}) + }, + 'catalogue.tagrelation': { + 'Meta': {'unique_together': "(('tag', 'content_type', 'object_id'),)", 'object_name': 'TagRelation', 'db_table': "'catalogue_tag_relation'"}, + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}), + 'tag': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'items'", 'to': "orm['catalogue.Tag']"}) + }, + 'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + } + } + + complete_apps = ['catalogue'] diff --git a/apps/catalogue/models.py b/apps/catalogue/models.py index e4d359500..565b759ce 100644 --- a/apps/catalogue/models.py +++ b/apps/catalogue/models.py @@ -22,7 +22,7 @@ from django.conf import settings from newtagging.models import TagBase, tags_updated from newtagging import managers from catalogue.fields import JSONField, OverwritingFileField -from catalogue.utils import ExistingFile, BookImportDocProvider, create_zip_task, remove_zip +from catalogue.utils import ExistingFile, ORMDocProvider, create_zip_task, remove_zip from librarian import dcparser, html, epub, NoDublinCore import mutagen @@ -296,11 +296,8 @@ class Book(models.Model): gazeta_link = models.CharField(blank=True, max_length=240) wiki_link = models.CharField(blank=True, max_length=240) # files generated during publication - xml_file = models.FileField(_('XML file'), upload_to=book_upload_path('xml'), blank=True) - html_file = models.FileField(_('HTML file'), upload_to=book_upload_path('html'), blank=True) - pdf_file = models.FileField(_('PDF file'), upload_to=book_upload_path('pdf'), blank=True) - epub_file = models.FileField(_('EPUB file'), upload_to=book_upload_path('epub'), blank=True) - txt_file = models.FileField(_('TXT file'), upload_to=book_upload_path('txt'), blank=True) + + file_types = ['epub', 'html', 'mobi', 'pdf', 'txt', 'xml'] parent = models.ForeignKey('self', blank=True, null=True, related_name='children') objects = models.Manager() @@ -351,49 +348,15 @@ class Book(models.Model): return book_tag def has_media(self, type): - if type == 'xml': - if self.xml_file: - return True - else: - return False - elif type == 'html': - if self.html_file: - return True - else: - return False - elif type == 'txt': - if self.txt_file: - return True - else: - return False - elif type == 'pdf': - if self.pdf_file: - return True - else: - return False - elif type == 'epub': - if self.epub_file: - return True - else: - return False + if type in Book.file_types: + return bool(getattr(self, "%s_file" % type)) else: - if self.media.filter(type=type).exists(): - return True - else: - return False + return self.media.filter(type=type).exists() def get_media(self, type): if self.has_media(type): - if type == "xml": - return self.xml_file - elif type == "html": - return self.html_file - elif type == "epub": - return self.epub_file - elif type == "txt": - return self.txt_file - elif type == "pdf": - return self.pdf_file + if type in Book.file_types: + return getattr(self, "%s_file" % type) else: return self.media.filter(type=type) else: @@ -433,11 +396,13 @@ class Book(models.Model): tags = [mark_safe(u'%s' % (tag.get_absolute_url(), tag.name)) for tag in tags] formats = [] - # files generated during publication + # files generated during publication if self.has_media("html"): formats.append(u'%s' % (reverse('book_text', kwargs={'slug': self.slug}), _('Read online'))) if self.has_media("pdf"): formats.append(u'PDF' % self.get_media('pdf').url) + if self.has_media("mobi"): + formats.append(u'MOBI' % self.get_media('mobi').url) if self.root_ancestor.has_media("epub"): formats.append(u'EPUB' % self.root_ancestor.get_media('epub').url) if self.has_media("txt"): @@ -473,26 +438,6 @@ class Book(models.Model): has_description.boolean = True # ugly ugly ugly - def has_pdf_file(self): - return bool(self.pdf_file) - has_pdf_file.short_description = 'PDF' - has_pdf_file.boolean = True - - def has_epub_file(self): - return bool(self.epub_file) - has_epub_file.short_description = 'EPUB' - has_epub_file.boolean = True - - def has_txt_file(self): - return bool(self.txt_file) - has_txt_file.short_description = 'HTML' - has_txt_file.boolean = True - - def has_html_file(self): - return bool(self.html_file) - has_html_file.short_description = 'HTML' - has_html_file.boolean = True - def has_odt_file(self): return bool(self.has_media("odt")) has_odt_file.short_description = 'ODT' @@ -524,10 +469,9 @@ class Book(models.Model): # remove zip with all pdf files remove_zip(settings.ALL_PDF_ZIP) - path, fname = os.path.realpath(self.xml_file.path).rsplit('/', 1) try: pdf_file = NamedTemporaryFile(delete=False) - pdf.transform(BookImportDocProvider(self), + pdf.transform(ORMDocProvider(self), file_path=str(self.xml_file.path), output_file=pdf_file, ) @@ -536,6 +480,28 @@ class Book(models.Model): finally: unlink(pdf_file.name) + def build_mobi(self): + """ (Re)builds the MOBI file. + + """ + from librarian import mobi + from tempfile import NamedTemporaryFile + import os + + # remove zip with all pdf files + remove_zip(settings.ALL_MOBI_ZIP) + + try: + mobi_file = NamedTemporaryFile(suffix='.mobi', delete=False) + mobi.transform(ORMDocProvider(self), verbose=1, + file_path=str(self.xml_file.path), + output_file=mobi_file.name, + ) + + self.mobi_file.save('%s.mobi' % self.slug, File(open(mobi_file.name))) + finally: + unlink(mobi_file.name) + def build_epub(self, remove_descendants=True): """ (Re)builds the epub file. If book has a parent, does nothing. @@ -554,7 +520,7 @@ class Book(models.Model): epub_file = StringIO() try: - epub.transform(BookImportDocProvider(self), self.slug, output_file=epub_file) + epub.transform(ORMDocProvider(self), self.slug, output_file=epub_file) self.epub_file.save('%s.epub' % self.slug, ContentFile(epub_file.getvalue())) FileRecord(slug=self.slug, type='epub', sha1=sha1(epub_file.getvalue()).hexdigest()).save() except NoDublinCore: @@ -660,6 +626,15 @@ class Book(models.Model): result = create_zip_task(paths, settings.ALL_PDF_ZIP) return result + @staticmethod + def zip_mobi(): + books = Book.objects.all() + + paths = filter(lambda x: x is not None, + map(lambda b: b.mobi_file and b.mobi_file.path or None, books)) + result = create_zip_task.delay(paths, settings.ALL_MOBI_ZIP) + return settings.MEDIA_URL + result.wait() + def zip_audiobooks(self): bm = BookMedia.objects.filter(book=self) paths = map(lambda bm: bm.file.path, bm) @@ -670,7 +645,6 @@ class Book(models.Model): result = create_zip_task(paths, self.slug) return result - @classmethod def from_xml_file(cls, xml_file, **kwargs): # use librarian to parse meta-data @@ -685,7 +659,8 @@ class Book(models.Model): xml_file.close() @classmethod - def from_text_and_meta(cls, raw_file, book_info, overwrite=False, build_epub=True, build_txt=True, build_pdf=True): + def from_text_and_meta(cls, raw_file, book_info, overwrite=False, + build_epub=True, build_txt=True, build_pdf=True, build_mobi=True): import re # check for parts before we do anything @@ -761,6 +736,9 @@ class Book(models.Model): if not settings.NO_BUILD_PDF and build_pdf: book.root_ancestor.build_pdf() + if not settings.NO_BUILD_MOBI and build_mobi: + book.build_mobi() + book_descendants = list(book.children.all()) # add l-tag to descendants and their fragments # delete unnecessary EPUB files @@ -872,6 +850,24 @@ class Book(models.Model): return objects +def _has_factory(ftype): + has = lambda self: bool(getattr(self, "%s_file" % ftype)) + has.short_description = t.upper() + has.boolean = True + has.__name__ = "has_%s_file" % ftype + return has + + +# add the file fields +for t in Book.file_types: + field_name = "%s_file" % t + models.FileField(_("%s file" % t.upper()), + upload_to=book_upload_path(t), + blank=True).contribute_to_class(Book, field_name) + + setattr(Book, "has_%s_file" % t, _has_factory(t)) + + class Fragment(models.Model): text = models.TextField() short_text = models.TextField(editable=False) diff --git a/apps/catalogue/test_utils.py b/apps/catalogue/test_utils.py index 7905efbb3..48e9c90ad 100644 --- a/apps/catalogue/test_utils.py +++ b/apps/catalogue/test_utils.py @@ -10,7 +10,8 @@ class WLTestCase(TestCase): """ def setUp(self): self._MEDIA_ROOT, settings.MEDIA_ROOT = settings.MEDIA_ROOT, tempfile.mkdtemp(prefix='djangotest_') - settings.NO_BUILD_PDF = settings.NO_BUILD_EPUB = settings.NO_BUILD_TXT = True + settings.NO_BUILD_PDF = settings.NO_BUILD_MOBI = settings.NO_BUILD_EPUB = settings.NO_BUILD_TXT = True + settings.USE_CELERY = False def tearDown(self): shutil.rmtree(settings.MEDIA_ROOT, True) diff --git a/apps/catalogue/urls.py b/apps/catalogue/urls.py index 7fd265fdc..fbb1618f7 100644 --- a/apps/catalogue/urls.py +++ b/apps/catalogue/urls.py @@ -25,6 +25,7 @@ urlpatterns = patterns('catalogue.views', # zip url(r'^zip/pdf/.*\.zip$', 'download_zip', {'format': 'pdf', 'slug': None}, 'download_zip_pdf'), url(r'^zip/epub/.*\.zip$', 'download_zip', {'format': 'epub', 'slug': None}, 'download_zip_epub'), + url(r'^zip/mobi/.*\.zip$', 'download_zip', {'format': 'mobi', 'slug': None}, 'download_zip_mobi'), url(r'^zip/audiobook/(?P[a-zA-Z0-9-]+)\.zip', 'download_zip', {'format': 'audiobook'}, 'download_zip_audiobook'), # tools diff --git a/apps/catalogue/utils.py b/apps/catalogue/utils.py index 07458c9ac..3a8d1b027 100644 --- a/apps/catalogue/utils.py +++ b/apps/catalogue/utils.py @@ -55,8 +55,8 @@ class ExistingFile(UploadedFile): pass -class BookImportDocProvider(DocProvider): - """Used for joined EPUB and PDF files.""" +class ORMDocProvider(DocProvider): + """Used for getting books' children.""" def __init__(self, book): self.book = book diff --git a/apps/catalogue/views.py b/apps/catalogue/views.py index 90ef8c2c1..9168099ca 100644 --- a/apps/catalogue/views.py +++ b/apps/catalogue/views.py @@ -606,7 +606,7 @@ def download_shelf(request, slug): if form.is_valid(): formats = form.cleaned_data['formats'] if len(formats) == 0: - formats = ['pdf', 'epub', 'odt', 'txt'] + formats = ['pdf', 'epub', 'mobi', 'odt', 'txt'] # Create a ZIP archive temp = tempfile.TemporaryFile() @@ -617,6 +617,9 @@ def download_shelf(request, slug): if 'pdf' in formats and book.pdf_file: filename = book.pdf_file.path archive.write(filename, str('%s.pdf' % book.slug)) + if 'mobi' in formats and book.mobi_file: + filename = book.mobi_file.path + archive.write(filename, str('%s.mobi' % 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)) @@ -646,13 +649,15 @@ def shelf_book_formats(request, shelf): """ shelf = get_object_or_404(models.Tag, slug=shelf, category='set') - formats = {'pdf': False, 'epub': False, 'odt': False, 'txt': False} + formats = {'pdf': False, 'epub': False, 'mobi': False, 'odt': False, 'txt': 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.mobi_file: + formats['mobi'] = True if book.txt_file: formats['txt'] = True for format in ('odt',): @@ -780,6 +785,8 @@ def download_zip(request, format, slug): url = models.Book.zip_pdf() elif format == 'epub': url = models.Book.zip_epub() + elif format == 'mobi': + url = models.Book.zip_mobi() elif format == 'audiobook' and slug is not None: book = models.Book.objects.get(slug=slug) url = book.zip_audiobooks() diff --git a/lib/librarian b/lib/librarian index a6ee2dd83..f8442984a 160000 --- a/lib/librarian +++ b/lib/librarian @@ -1 +1 @@ -Subproject commit a6ee2dd83d3c4d5d2d3e8cb3401734ced2b12c22 +Subproject commit f8442984a021793164661a54ec84ae9caaf9862b diff --git a/wolnelektury/settings.py b/wolnelektury/settings.py index 826d1110d..0b9fcdfa0 100644 --- a/wolnelektury/settings.py +++ b/wolnelektury/settings.py @@ -227,9 +227,11 @@ MAX_TAG_LIST = 6 NO_BUILD_EPUB = False NO_BUILD_TXT = False NO_BUILD_PDF = False +NO_BUILD_MOBI = False ALL_EPUB_ZIP = 'wolnelektury_pl_epub' ALL_PDF_ZIP = 'wolnelektury_pl_pdf' +ALL_MOBI_ZIP = 'wolnelektury_pl_mobi' PAGINATION_INVALID_PAGE_RAISES_404 = True diff --git a/wolnelektury/static/img/mobi.png b/wolnelektury/static/img/mobi.png new file mode 100644 index 000000000..b278ec29e Binary files /dev/null and b/wolnelektury/static/img/mobi.png differ diff --git a/wolnelektury/templates/catalogue/book_detail.html b/wolnelektury/templates/catalogue/book_detail.html index 11f16d046..ff7b5199a 100644 --- a/wolnelektury/templates/catalogue/book_detail.html +++ b/wolnelektury/templates/catalogue/book_detail.html @@ -46,13 +46,16 @@ {% trans "Read online" %} {% endif %}
- {% if book.has_pdf_file %} + {% if book.pdf_file %} {% trans {% endif %} {% if book.root_ancestor.epub_file %} {% trans {% endif %} - {% if book.has_txt_file %} + {% if book.mobi_file %} + {% trans + {% endif %} + {% if book.txt_file %} {% trans {% endif %} {% for media in book.get_odt %} diff --git a/wolnelektury/templates/catalogue/tagged_object_list.html b/wolnelektury/templates/catalogue/tagged_object_list.html index 4809ad1ce..fec910530 100644 --- a/wolnelektury/templates/catalogue/tagged_object_list.html +++ b/wolnelektury/templates/catalogue/tagged_object_list.html @@ -36,6 +36,7 @@

{% trans "Choose books' formats which you want to download:" %}

  • {% trans "for reading" %} {% trans "and printing using" %} Adobe Reader
  • {% trans "for reading" %} {% trans "on mobile devices" %}
  • +
  • {% trans "for reading" %} {% trans "on mobile devices" %}
  • {% trans "for reading" %} {% trans "and editing using" %} OpenOffice.org
  • {% trans "for reading" %} {% trans "on small displays, for example mobile phones" %}
  • {% trans "for listening" %} {% trans "on favourite MP3 player" %}