Compare commits

...

12 Commits

Author SHA1 Message Date
Michael Lehmann
1c111fd637 Remove unused imports 2025-10-06 23:27:40 +02:00
Michael Lehmann
777cff600c Ignore type error. 2025-10-06 23:21:29 +02:00
Michael Lehmann
be6bfb10cd Update 2025-10-06 23:14:56 +02:00
Michael Lehmann
ca76bdd564 Add generate epub function. 2025-10-05 15:58:46 +02:00
Michael Lehmann
52d03aa633 Add types-requests. 2025-10-05 14:45:34 +02:00
Michael Lehmann
2435bd7888 Work in progress. 2025-10-05 14:45:24 +02:00
Michael Lehmann
71e6c5dd48 Add Pip. 2025-10-05 14:43:04 +02:00
Michael Lehmann
827a2ae354 Add intial main.py file 2025-10-05 12:50:53 +02:00
Michael Lehmann
31e2a102bd Add additional Packages 2025-10-05 12:50:41 +02:00
Michael Lehmann
3f4c3e8617 Update readme. 2025-10-05 12:33:46 +02:00
Michael Lehmann
470662be1b Remove Git Hook for Markdown 2025-10-05 12:29:24 +02:00
Michael Lehmann
029a68032f Add initial DevEnv. 2025-10-05 12:22:21 +02:00
7 changed files with 440 additions and 2 deletions

10
.envrc Normal file
View File

@@ -0,0 +1,10 @@
export DIRENV_WARN_TIMEOUT=20s
eval "$(devenv direnvrc)"
# `use devenv` supports the same options as the `devenv shell` command.
#
# To silence the output, use `--quiet`.
#
# Example usage: use devenv --quiet --impure --option services.postgres.enable:bool true
use devenv

10
.gitignore vendored
View File

@@ -182,3 +182,13 @@ result-*
# Ignore automatically generated direnv output
.direnv
# Devenv
.devenv*
devenv.local.nix
# direnv
.direnv
# pre-commit
.pre-commit-config.yaml

View File

@@ -1,3 +1,54 @@
# issuu2epub
# Issuu to EPUB Converter
Download Documents from Issuu as EPUB Files.
Ein Python-Skript zum Konvertieren von Issuu-Dokumenten in EPUB-Dateien,
optimiert für Kindle E-Reader.
## 📋 Übersicht
Dieses Tool lädt Dokumente von Issuu herunter und konvertiert sie in das
EPUB-Format. Das erste Bild wird als Cover verwendet, und alle Bilder werden für
optimale Darstellung auf E-Readern komprimiert.
## ✨ Features
- **Einfache Befehlszeilenschnittstelle** mit Click
- **Automatisches Cover** aus der ersten Seite
- **Kindle-optimierte** Bildkompression
- **Kein Inhaltsverzeichnis** für direkten Zugriff auf den Inhalt
- **Temporäre Verzeichnisse** für saubere Verarbeitung
- **Unterstützt große Dokumente** mit Stream-Download
## 🚀 Verwendung
### Einfache Verwendung
```bash
python issuu2epub.py
```
Das Skript fragt interaktiv nach allen benötigten Informationen:
1. Issuu URL: Die URL des Issuu-Dokuments
2. Document Title: Titel des Dokuments
3. Document Author: Autor des Dokuments
4. EPUB Output Filename: Name der Ausgabedatei (z.B. mein_dokument.epub)
### Direkte Befehlszeilenverwendung
```bash
python issuu2epub.py --url "https://issuu.com/bscyb1898/docs/yb_mag_nr._1_saison_2025_26" --title "YB Mag 2025 01" --author "BSC YB" --output "YB_Mag_2025_01.epub"
```
## Notes
Dieses Tool ist für den persönlichen Gebrauch bestimmt. Stellen Sie sicher, dass
Sie die Urheberrechte der Dokumente respektieren und nur Inhalte verwenden, für
die Sie die entsprechenden Rechte besitzen oder die unter einer freien Lizenz
stehen.
## Credits
- AhmedOsamaMath/issuu-downloader
```
```

103
devenv.lock Normal file
View File

@@ -0,0 +1,103 @@
{
"nodes": {
"devenv": {
"locked": {
"dir": "src/modules",
"lastModified": 1759625163,
"owner": "cachix",
"repo": "devenv",
"rev": "aa9968386bc65519c174cfef1ae4b3464c19ba0a",
"type": "github"
},
"original": {
"dir": "src/modules",
"owner": "cachix",
"repo": "devenv",
"type": "github"
}
},
"flake-compat": {
"flake": false,
"locked": {
"lastModified": 1747046372,
"owner": "edolstra",
"repo": "flake-compat",
"rev": "9100a0f413b0c601e0533d1d94ffd501ce2e7885",
"type": "github"
},
"original": {
"owner": "edolstra",
"repo": "flake-compat",
"type": "github"
}
},
"git-hooks": {
"inputs": {
"flake-compat": "flake-compat",
"gitignore": "gitignore",
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1759523803,
"owner": "cachix",
"repo": "git-hooks.nix",
"rev": "cfc9f7bb163ad8542029d303e599c0f7eee09835",
"type": "github"
},
"original": {
"owner": "cachix",
"repo": "git-hooks.nix",
"type": "github"
}
},
"gitignore": {
"inputs": {
"nixpkgs": [
"git-hooks",
"nixpkgs"
]
},
"locked": {
"lastModified": 1709087332,
"owner": "hercules-ci",
"repo": "gitignore.nix",
"rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "gitignore.nix",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1758532697,
"owner": "cachix",
"repo": "devenv-nixpkgs",
"rev": "207a4cb0e1253c7658c6736becc6eb9cace1f25f",
"type": "github"
},
"original": {
"owner": "cachix",
"ref": "rolling",
"repo": "devenv-nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"devenv": "devenv",
"git-hooks": "git-hooks",
"nixpkgs": "nixpkgs",
"pre-commit-hooks": [
"git-hooks"
]
}
}
},
"root": "root",
"version": 7
}

34
devenv.nix Normal file
View File

@@ -0,0 +1,34 @@
{pkgs, ...}: {
languages = {
python = {
enable = true;
package = pkgs.python313;
};
};
packages = [
pkgs.python313Packages.click
pkgs.python313Packages.ebooklib
pkgs.python313Packages.requests
pkgs.python313Packages.pip
pkgs.python313Packages.pillow
pkgs.python313Packages.types-requests
];
git-hooks = {
enable = true;
hooks = {
black = {
enable = true;
};
isort = {
enable = true;
};
mypy = {
enable = true;
};
};
};
}

15
devenv.yaml Normal file
View File

@@ -0,0 +1,15 @@
# yaml-language-server: $schema=https://devenv.sh/devenv.schema.json
inputs:
nixpkgs:
url: github:cachix/devenv-nixpkgs/rolling
# If you're using non-OSS software, you can set allowUnfree to true.
# allowUnfree: true
# If you're willing to use a package that's vulnerable
# permittedInsecurePackages:
# - "openssl-1.1.1w"
# If you have more than one devenv you can merge them
#imports:
# - ./backend

215
issuu2epub.py Normal file
View File

@@ -0,0 +1,215 @@
import io
import re
import secrets
import tempfile
from pathlib import Path
import click
from ebooklib import epub # type: ignore
from PIL import Image
from requests import HTTPError, request
def parse_issuu_url(url: str) -> tuple[str, str]:
"""Get Username and document_id from issuu url.
returns:
username: str
document_id: str
"""
issuu_url_pattern = re.compile(r"https://issuu.com/([^\/]*)/docs/(.*)$")
if mtc := issuu_url_pattern.match(url):
username = mtc.group(1)
document_id = mtc.group(2)
else:
raise ValueError("Issuu URL not Valid!")
return username, document_id
def create_working_dir() -> Path:
"""create a working directory.
returns:
Path() to a temporary directory.
"""
working_dir = tempfile.mkdtemp(prefix="issuu2epub_")
return Path(working_dir)
def get_page_urls(username: str, document_id: str) -> list[str]:
"""get a list of all pages."""
json_url = f"https://reader3.isu.pub/{username}/{document_id}/reader3_4.json"
r = request("GET", json_url, timeout=(5, 5))
if not r.ok:
raise HTTPError("Failed to download document information")
document_data = r.json()
return [
f"https://{page['imageUri']}" for page in document_data["document"]["pages"]
]
def download_pages(page_urls: list[str], working_dir: Path) -> list[Path]:
"""download all page images and return file paths."""
page_paths = []
for url in page_urls:
filename = url.split("/")[-1]
path = Path(working_dir / filename)
page_paths.append(path)
with request("GET", url=url, stream=True, timeout=(10, 10)) as r:
r.raise_for_status()
with open(path, "wb") as f:
for chunk in r.iter_content(chunk_size=8192):
if chunk:
f.write(chunk)
return page_paths
def convert_image(image_path: Path) -> io.BytesIO:
"""convert image and return bytes array."""
max_image_size = (1000, 1400)
target_quality = 50
with Image.open(image_path.as_posix()) as img:
if img.mode in ("RGBA", "P"):
img = img.convert("RGB")
img.thumbnail(max_image_size, Image.Resampling.LANCZOS)
img_byte_arr = io.BytesIO()
img.save(img_byte_arr, format="JPEG", optimize=True, quality=target_quality)
img_byte_arr = img_byte_arr.getvalue() # type: ignore[assignment]
return img_byte_arr # type: ignore[return-value]
def generate_epub(
pages: list[Path], output_file: Path, title: str, author: str
) -> None:
"""generate epub file."""
book = epub.EpubBook()
book.set_identifier(secrets.token_urlsafe(10))
book.set_title(title=title)
book.set_language("de")
book.add_author(author=author)
chapters = []
# Use first image as Cover
title_page = epub.EpubHtml(title=title, file_name="title_page.xhtml", lang="de")
cover_image = epub.EpubImage()
cover_image.file_name = f"images/cover.jpg"
cover_image.media_type = "image/jpeg"
cover_image.content = convert_image(pages[0])
book.add_item(cover_image)
title_page.content = f"""
<html>
<head>
<title>{title}</title>
<style>
body {{
margin: 0;
padding: 20px;
text-align: center;
}}
img {{
max-width: 100%;
height: auto;
max-height: 90vh;
}}
</style>
</head>
<body>
<img src="{cover_image.file_name}" alt="Cover"/>
</body>
</html>
"""
book.add_item(title_page)
chapters.append(title_page)
# Add Pages.
for i, page in enumerate(pages[1:], start=1):
page_title = f"Page {i}"
image_item = epub.EpubImage()
image_item.file_name = f"images/page_{i:03d}.jpg"
image_item.media_type = "image/jpeg"
image_item.content = convert_image(page)
book.add_item(image_item)
chapter = epub.EpubHtml(
title=page_title, file_name=f"page_{i}.xhtml", lang="de"
)
chapter.content = f"""
<html>
<head>
<title>{page_title}</title>
<style>
body {{
margin: 0;
padding: 20px;
text-align: center;
}}
img {{
max-width: 100%;
height: auto;
max-height: 90vh;
}}
</style>
</head>
<body>
<img src="{image_item.file_name}" alt="Page {i}"/>
</body>
</html>
"""
book.add_item(chapter)
chapters.append(chapter)
book.spine = ["nav"] + chapters
epub.write_epub(output_file, book, {})
print(f"✅ Kindle-optimiertes EPUB erstellt: {output_file}")
@click.command()
@click.option("--url", prompt="Issuu URL", help="Issuu URL to convert to EPUB")
@click.option("--title", prompt="Document Title", help="Document Title")
@click.option("--author", prompt="Document Author", help="Document Author")
@click.option(
"--output",
prompt="EPUB Output Filename",
help="EPUB Output File",
)
def main(url: str, title: str, author: str, output: str) -> None:
"""main function."""
cwd = create_working_dir()
username, document_id = parse_issuu_url(url=url)
urls = get_page_urls(username, document_id)
pages = download_pages(urls, cwd)
generate_epub(
pages=pages,
output_file=Path(output),
title=title,
author=author,
)
if __name__ == "__main__":
main()