aerkalov / ebooklib

Python E-book library for handling books in EPUB2/EPUB3 format -
https://ebooklib.readthedocs.io/
GNU Affero General Public License v3.0
1.49k stars 234 forks source link

Unable to get my epub read on google play books! #314

Closed ghost closed 4 months ago

ghost commented 4 months ago

For some reason, when I add the cover to the spine, the EPUB doesn't work on Google Play Books. It works on all other EPUB readers except Google Play Books. This might be related to EPUB 2 support. However, if I convert the EPUB to another format using Calibre and then convert it back to EPUB, it updates correctly. Additionally, if I remove the "self.cover_page" from the spine, it also works.

In the momment i run this function, it doesn't work anymore if you comment it, it works

    def write_cover_page(self):
        self.cover_page = epub.EpubHtml(title="Cover Page", file_name="cover.xhtml")
        self.cover_page.content = ( f"<img src='cover.jpeg'/>" )
        self.book.add_item(self.cover_page)

I also get this error that i'm not actually sure how to fix when the self.cover_page is on the spine and the function is ran

/usr/lib/python3.12/zipfile/__init__.py:1607: UserWarning: Duplicate name: 'EPUB/cover.xhtml'
  return self._open_to_write(zinfo, force_zip64=force_zip64)
from ebooklib import epub

CONTENT = [['TitleTest", "test2", "test3", "test4"]]
CONTENT_INDEX = 0
INDENT = "id123456DIADJWIAJDIADAWDSD"
BOOK_TITLE = "It Doesn't matter title"
LANG = "en"
COVER_PATH = "test-cover.jpeg"
AUTHOR = "It Doesn't matter author"
VOLUME_TITLE = "volume 01"
FILE_NAME = "volume 01"

class EpubGenerator:
    def __init__(self):
        self.book = epub.EpubBook()

    def write_meta_data(self, indent: str, book_title: str, author: str, lang: str):
        self.book.set_identifier(indent)
        self.book.set_title(book_title)
        self.book.add_author(author)
        self.book.set_language(lang)

    def write_cover(self, cover_path: str):
        with open(cover_path, 'rb') as cover_file:
            cover_content = cover_file.read()
        self.book.set_cover("cover.jpeg", cover_content)

    def write_cover_page(self):
        self.cover_page = epub.EpubHtml(title="Cover Page", file_name="cover.xhtml")
        self.cover_page.content = ( f"<img src='cover.jpeg'/>" )
        self.book.add_item(self.cover_page)

    def write_chapter(self, volume_title: str, file_name: str, content: list, content_index: int):
        self.chapter = epub.EpubHtml(title=volume_title, file_name=f"{file_name}.xhtml")
        chapter_content = f"<h1>{content[content_index][0]}</h1>"

        for item in content[content_index][1:]:
            chapter_content += f"<p>{item}</p>"

        self.chapter.content = chapter_content

        self.book.add_item(self.chapter)

    def write_table_of_content(self, volume_title: str):
        self.book.toc = (
            epub.Link(f"{volume_title}.xhtml", volume_title, volume_title),
        )

    def write_epub(self, filename: str):
        self.book.add_item(epub.EpubNav())
        self.book.add_item(epub.EpubNcx())
        self.book.spine = [self.cover_page, self.chapter]

        epub.write_epub(f"{filename}.epub", self.book, {})

epub_generator = EpubGenerator()
epub_generator.write_meta_data(indent=INDENT, book_title=BOOK_TITLE, author=AUTHOR, lang=LANG)
epub_generator.write_cover(cover_path=COVER_PATH)
epub_generator.write_cover_page()
epub_generator.write_chapter(volume_title=VOLUME_TITLE, file_name=FILE_NAME, content=CONTENT, content_index=CONTENT_INDEX)
epub_generator.write_table_of_content(volume_title=VOLUME_TITLE)
epub_generator.write_epub(filename=FILE_NAME)
aerkalov commented 4 months ago

Check the samples/02_cover_create/create.py. You don't need to create cover page (if it is just image on the cover):

book.set_cover("cover.jpg", cover_content, create_page=True)

This is the template for the cover page:

COVER_XML = six.b('''<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:epub="http://www.idpf.org/2007/ops" lang="en" xml:lang="en">
 <head>
  <style>
    body { margin: 0em; padding: 0em; }
    img { max-width: 100%; max-height: 100%; }
  </style>
 </head>
 <body>
   <img src="" alt="" />
 </body>
</html>''')

This should do the trick and you can just add 'cover' to the spine (like in the sample). Did you tried to use epubcheck tool to validate your EPUB file?

That being said, I noticed now that sample creates EPUB which fails with epubcheck 5.0 and EPUB 3.3. Just created new ticket (#315 ) for it and will take a look at it tomorrow.

Aco

ghost commented 4 months ago

Hi,

Thank you for the information.

Simply changing the name "cover.xhtml" to something else resolved the issue for me. Could it be that there's something on epub's 3.3 side already using the cover as name?

    def write_cover_page(self):
        self.cover_page = epub.EpubHtml(title="Cover Page", file_name="cover.xhtml")
        self.cover_page.content = ( f"<img src='cover.jpeg'/>" )
        self.book.add_item(self.cover_page)
    def write_cover_page(self):
        self.cover_page = epub.EpubHtml(title="Cover Page", file_name="titlepage.xhtml")
        self.cover_page.content = (f"<img src='cover_image.jpeg'/>")
        self.book.add_item(self.cover_page)
aerkalov commented 4 months ago

Yes, when you call book.set_cover() it will create page with id="cover" and file_name="cover.xhtml". So in your case you don't need to create separate cover page.

Also, if you will have some issues with some checks according to the specs you would need to have cover page mentioned somewhere in the content or inside your TOC. In that case just fetch that page and insert it. For instance:

cover = book.get_item_with_id('cover')
book.toc = (
    cover, 
    epub.Link('intro.xhtml', 'Introduction', 'intro'),
    (epub.Section('Languages'),
      (c1, c2)
     )
)