❮
❯

[FAQ] Как быстро сделать постер и примеры страниц для книжной раздачи (PDF, DjVu, EPUB, FB2, MOBI)

Ответить на тему
 
Автор Сообщение

admin ®

Скрыт

Пол: Мужской

Зарегистрированный: 2 года назад

Сообщений: 224

Откуда: UZ

Наличие запрета:  Запретов нет

Медали (10):
Аниме (1) Активный релизер 1 (1) Admin (1) Активный сидер 1 (1) Активный сидер 2 (1)

Профиль  ЛС 

Создавать темы 24-сен-2026 10:00 | #1 · Автор

Код 

Постер и примеры страниц для раздачи
Инструкция по использованию скрипта
-Для книжной раздачи нужны обложка и несколько примеров страниц. Снимать их скриншотами долго, а качество получается хуже, чем в самой книге.
Ниже — готовый скрипт и инструкция, как им пользоваться. Вы перетаскиваете файл книги на ярлык, и рядом с книгой появляется папка с обложкой и примерами страниц в JPG. Одна книга обрабатывается за пару секунд, можно закинуть сразу несколько файлов.
Скрипт работает с PDF, DjVu, EPUB, FB2 и MOBI. Кроме картинок он показывает количество страниц в книге — его сразу можно вставлять в шаблон раздачи.
-

1. Что нужно для работы

Windows. Инструкция написана для Windows 10 и 11.
Python. Скачайте установщик с python.org и установите с настройками по умолчанию. Галочку «Add python.exe to PATH» ставить необязательно: используется команда py, она работает и без этого.
Библиотека PyMuPDF. Откройте командную строку (Win + R → введите cmd → Enter) и выполните:
py -m pip install pymupdf
Дождитесь строки Successfully installed pymupdf. Эта библиотека открывает PDF, EPUB, FB2 и MOBI.
DjVuLibre — только для DjVu. Если вы не выкладываете DjVu, этот пункт можно пропустить. Установщик для Windows: sourceforge.net/projects/djvu/files/DjVuLibre_Windows. Или одной командой в командной строке:
winget install --id DjVuLibre.DjView -e
После установки скрипт найдёт DjVuLibre сам.
Свободное место. Картинки весят немного: комплект из обложки и четырёх страниц занимает примерно 1,5–3 МБ.

2. Установка

Шаг 1. Создайте любую папку, например D:\Скрины.
Шаг 2. Создайте в ней файл pdf_shots.py и вставьте в него код из пункта 9.
Шаг 3. Создайте в той же папке файл Скрины.bat с таким содержимым:
@echo off
py "%~dp0pdf_shots.py" %*
pause
Шаг 4. Проверьте имена файлов. Блокнот по умолчанию дописывает .txt, а Windows расширения прячет. Включите их: Вид → Показать → Расширения имён файлов. В папке должны лежать ровно pdf_shots.py и Скрины.bat. При сохранении в Блокноте выбирайте «Тип файла: Все файлы» и кодировку UTF-8.
Папку можно положить куда угодно, книги рядом с ней держать не нужно.

3. Как пользоваться

Способ 1 — перетаскиванием. Выделите одну или несколько книг и перетащите мышкой на файл Скрины.bat.
Способ 2 — через окно выбора. Запустите Скрины.bat двойным щелчком. Откроется окно выбора файлов, отметьте книги и нажмите «Открыть».
Пока скрипт работает, в чёрном окне видно, что происходит:
Обрабатываю: Handbook of Pediatric Neurosurgery.pdf
Страниц: 556
Готово: D:\Книги\Handbook of Pediatric Neurosurgery_скрины
Окно само не закроется — нажмите Enter, когда всё прочитаете.

4. Что получится

Рядом с книгой появится папка с её именем и припиской _скрины:
  • poster.jpg — обложка книги;
  • page_35.jpg, page_128.jpg и ещё две — примеры страниц, в имени файла стоит номер страницы.
Страницы берутся из середины книги, первые и последние не используются, поэтому в примеры не попадают титул, оглавление и указатель. В первую очередь скрипт берёт страницы с иллюстрациями, пустые пропускает.
Набор страниц каждый раз разный. Если картинки не понравились, просто перетащите книгу на батник ещё раз.

5. Настройки

Если нужно, откройте pdf_shots.py в Блокноте и поправьте первые строки:
DPI = 150          # качество картинок (110 — меньше размер, 200 — чётче)
SHOTS = 4          # сколько страниц сохранять
DJVU_DIR = r""     # папка DjVuLibre, если скрипт не нашёл её сам
DPI — качество. При 150 страница книжного формата получается примерно 1240 × 1750 пикселей и весит 300–600 КБ. Поставьте 110, если картинки слишком тяжёлые, или 200–300, если нужны чёткие иллюстрации.
SHOTS — сколько страниц сохранять, кроме обложки. Для атласов удобно 6–8.
DJVU_DIR — заполняется, только если скрипт пишет, что не нашёл DjVuLibre. Укажите папку, в которой лежит ddjvu.exe:
DJVU_DIR = r"C:\Program Files (x86)\DjVuLibre"
После правки сохраните файл и запустите скрипт заново.

6. Что важно знать про форматы

PDF и DjVu. Количество страниц, которое показывает скрипт, точное, его можно указывать в раздаче.
EPUB, FB2, MOBI. В этих форматах фиксированных страниц нет, текст подстраивается под экран. Скрипт пишет число со значком «~» и пометкой, что оно условное:
Страниц: ~681 (для EPUB число условное — зависит от размера экрана)
В раздаче в поле «Количество страниц» указывайте объём печатного издания с сайта издательства. Обложку для EPUB скрипт берёт из самой книги, это оригинальная картинка издательства в полном качестве.
PDF, сделанный из EPUB. Если в PDF страниц в 2–3 раза больше, чем в печатной книге (например, 758 вместо 258), файл почти наверняка сконвертирован из EPUB. Проверьте в SumatraPDF по Ctrl + D: у такого файла маленький размер страницы, а в поле Producer указан конвертер вроде Calibre. В раздаче это стоит указать: «PDF — конвертация из EPUB».
Сканы. Со сканированными PDF и DjVu скрипт работает так же. Качество картинок будет таким же, как качество скана.

7. Что делать с картинками дальше

Загрузите poster.jpg и страницы на хостинг изображений трекера, а ссылки вставьте в шаблон оформления раздачи. Сам шаблон и теги для обложки и примеров страниц разобраны в отдельной статье FAQ.

8. Если что-то не работает

Перетащил книгу на батник — ничего не происходит.
Скорее всего, файл называется Скрины.bat.txt: Блокнот дописал расширение, а Windows его прячет. Включите показ расширений (Вид → Показать → Расширения имён файлов) и уберите лишнее .txt. То же самое проверьте у pdf_shots.py.
Окно мелькнуло и закрылось.
В батнике последней строкой должно быть pause. Если вы запускали не батник, а сам pdf_shots.py, запускайте через батник: тогда окно останется открытым и будет видно сообщение об ошибке.
«py» не является внутренней или внешней командой.
Python не установлен либо установлен без лаунчера. Установите его с python.org. Если он уже стоит, запустите установщик ещё раз, выберите Modify, на экране Advanced Options отметьте «Add Python to environment variables» и нажмите Install. После этого перезапустите командную строку.
ModuleNotFoundError: No module named 'pymupdf'.
Библиотека не установлена или установлена в другую версию Python. Выполните в командной строке:
py -m pip install pymupdf
Важно писать именно py -m pip, а не просто pip: так библиотека попадёт в тот Python, который запускает скрипт.
Ошибка: Не найден DjVuLibre.
Установите DjVuLibre по ссылке из пункта 1. Если он уже установлен в нестандартную папку, впишите её в строку DJVU_DIR в начале скрипта. В этой папке должен лежать файл ddjvu.exe.
Картинки слишком тяжёлые или слишком мелкие.
Измените DPI в начале скрипта: 110 — компактнее, 200–300 — чётче. Сохраните файл и запустите скрипт заново, старую папку со скринами можно удалить.
Попались неудачные страницы.
Запустите скрипт на этой книге ещё раз, набор страниц будет другим. Либо увеличьте SHOTS, например до 8, и выберите лучшие картинки.
Книга защищена паролем.
Такой файл скрипт не откроет. Пароль он не подбирает и защиту не снимает.
В пути или имени файла русские буквы.
Это не мешает. Папка со скринами тоже создаётся с русским именем.

9. Код скрипта (pdf_shots.py)

Скопируйте всё содержимое в файл pdf_shots.py.
import sys, os, glob, shutil, random, pathlib, traceback, subprocess, zipfile, posixpath
import xml.etree.ElementTree as ET
from urllib.parse import unquote
try:
    import pymupdf
except ImportError:
    print("Не установлен pymupdf. Выполните в командной строке: py -m pip install pymupdf")
    input("\nНажмите Enter для выхода...")
    sys.exit()
DPI = 150          # качество картинок (110 — меньше размер, 200 — чётче)
SHOTS = 4          # сколько страниц сохранять
DJVU_DIR = r""     # если DjVuLibre не находится сам, укажите папку, например r"C:\Program Files (x86)\DjVuLibre"
TYPES = [("Книги", "*.pdf *.djvu *.djv *.epub *.fb2 *.mobi"), ("Все файлы", "*.*")]
def save_jpg(pix, path):
    if pix.colorspace is None or pix.colorspace.n != 3:
        pix = pymupdf.Pixmap(pymupdf.csRGB, pix)
    if pix.alpha:
        pix = pymupdf.Pixmap(pix, 0)
    pix.save(path, jpg_quality=90)
def middle_pool(n, first=0):
    """Номера страниц для выбора: без первых и последних 10, в случайном порядке."""
    pool = list(range(first + 1, first + n)) if n < 40 else list(range(first + 10, first + n - 10))
    random.shuffle(pool)
    return pool
# ---------- PDF, EPUB, FB2, MOBI ----------
def epub_cover(path):
    """Оригинальная обложка из EPUB (если указана в метаданных), иначе None."""
    try:
        with zipfile.ZipFile(path) as z:
            opf_path = ET.fromstring(z.read("META-INF/container.xml")).find(".//{*}rootfile").get("full-path")
            opf = ET.fromstring(z.read(opf_path))
            items = opf.findall(".//{*}manifest/{*}item")
            href = next((i.get("href") for i in items if "cover-image" in (i.get("properties") or "")), None)
            if not href:
                meta = next((m for m in opf.iter() if m.tag.endswith("meta") and m.get("name") == "cover"), None)
                if meta is not None:
                    href = next((i.get("href") for i in items if i.get("id") == meta.get("content")
                                 and (i.get("media-type") or "").startswith("image")), None)
            if not href:
                return None
            return z.read(posixpath.normpath(posixpath.join(posixpath.dirname(opf_path), unquote(href))))
    except Exception:
        return None
def page_kind(page):
    """2 — есть картинка, 1 — нормальный текст, 0 — пустая/почти пустая страница."""
    blocks = page.get_text("dict")["blocks"]
    if any(b["type"] == 1 for b in blocks):
        return 2
    chars = sum(len(s["text"]) for b in blocks if b["type"] == 0 for l in b["lines"] for s in l["spans"])
    return 1 if chars > 600 else 0
def process_doc(p, out):
    doc = pymupdf.open(p)
    if doc.is_reflowable:                      # EPUB, FB2, MOBI — раскладываем в страницы A5
        doc.layout(rect=pymupdf.paper_rect("a5"), fontsize=10)
    cover = epub_cover(p) if p.suffix.lower() == ".epub" else None
    try:
        save_jpg(pymupdf.Pixmap(cover), out / "poster.jpg")        # настоящая обложка из EPUB
    except Exception:
        save_jpg(doc[0].get_pixmap(dpi=DPI), out / "poster.jpg")   # иначе — первая страница
    pool = middle_pool(doc.page_count)
    with_img, text, rest = [], [], []
    for i in pool[:80]:
        k = page_kind(doc[i])
        (with_img if k == 2 else text if k == 1 else rest).append(i)
        if len(with_img) >= SHOTS:
            break
    for i in sorted((with_img + text + rest + pool[80:])[:SHOTS]):
        save_jpg(doc[i].get_pixmap(dpi=DPI), out / f"page_{i + 1}.jpg")
    if doc.is_reflowable:
        return f"~{doc.page_count} (для {p.suffix.upper()[1:]} число условное — зависит от размера экрана)"
    return str(doc.page_count)
# ---------- DjVu (через DjVuLibre) ----------
def djvu_tool(name):
    places = [DJVU_DIR] if DJVU_DIR else []
    for env in ("ProgramFiles", "ProgramFiles(x86)", "ProgramW6432"):
        if os.environ.get(env):
            places += glob.glob(os.path.join(os.environ[env], "DjVuLibre*"))
    if os.environ.get("LOCALAPPDATA"):
        places += glob.glob(os.path.join(os.environ["LOCALAPPDATA"], "Programs", "DjVuLibre*"))
    for folder in places:
        exe = os.path.join(folder, name + ".exe")
        if os.path.isfile(exe):
            return exe
    exe = shutil.which(name)
    if exe:
        return exe
    raise RuntimeError("Не найден DjVuLibre. Установите его: https://sourceforge.net/projects/djvu/files/DjVuLibre_Windows/\n"
                       "Если уже установлен в другую папку — впишите её в DJVU_DIR в начале скрипта.")
def djvu_structure(data):
    """Число страниц и объём фонового слоя (фото, рисунки) на каждой странице — по разметке файла."""
    if data[:4] != b"AT&T":
        raise RuntimeError("Это не DjVu-файл")
    bg, pages = {}, 0
    def walk(pos, end, page):
        nonlocal pages
        while pos + 8 <= end:
            cid, size = data[pos:pos + 4], int.from_bytes(data[pos + 4:pos + 8], "big")
            body = pos + 8
            if cid == b"FORM" and data[body:body + 4] == b"DJVU":
                pages += 1
                walk(body + 4, body + size, pages)
            elif cid == b"FORM" and data[body:body + 4] == b"DJVM":
                walk(body + 4, body + size, 0)
            elif cid == b"BG44" and page:
                bg[page] = bg.get(page, 0) + size
            pos = body + size + (size & 1)
    walk(4, len(data), 0)
    return max(pages, 1), bg
def process_djvu(p, out):
    data = p.read_bytes()   # файл передаётся через stdin — так не мешают русские буквы в пути
    ddjvu = djvu_tool("ddjvu")
    def run(args):
        r = subprocess.run(args, input=data, capture_output=True)
        if r.returncode != 0 or not r.stdout:
            raise RuntimeError(r.stderr.decode("utf-8", "replace").strip() or "ошибка DjVuLibre")
        return r.stdout
    def render(page, dpi):     # page — с единицы
        return pymupdf.Pixmap(run([ddjvu, "-format=ppm", f"-page={page}", f"-scale={dpi}", "-", "-"]))
    pages, bg = djvu_structure(data)
    def has_ink(page):         # отсекаем пустые страницы
        pix = render(page, 15)
        s, step = pix.samples, pix.n
        dark = sum(1 for k in range(0, len(s), step) if s[k] < 180)
        return dark > 0.02 * pix.width * pix.height
    save_jpg(render(1, DPI), out / "poster.jpg")
    pool = middle_pool(pages, first=1)
    pics = [x for x in pool if bg.get(x, 0) > 10000]
    ordered = pics + [x for x in pool if x not in set(pics)]
    picked = []
    for x in ordered[:40]:
        if has_ink(x):
            picked.append(x)
        if len(picked) == SHOTS:
            break
    picked += [x for x in ordered if x not in picked][:SHOTS - len(picked)]
    for x in sorted(picked):
        save_jpg(render(x, DPI), out / f"page_{x}.jpg")
    return str(pages)
# ---------- запуск ----------
def process(path):
    p = pathlib.Path(path)
    print(f"Обрабатываю: {p.name}")
    out = p.with_name(p.stem + "_скрины")
    out.mkdir(exist_ok=True)
    if p.suffix.lower() in (".djvu", ".djv"):
        count = process_djvu(p, out)
    else:
        count = process_doc(p, out)
    print(f"Страниц: {count}")
    print(f"Готово: {out}\n")
files = sys.argv[1:]
if not files:
    from tkinter import Tk, filedialog
    Tk().withdraw()
    files = filedialog.askopenfilenames(title="Выберите книги", filetypes=TYPES)
for f in files:
    try:
        process(f)
    except Exception as e:
        if isinstance(e, RuntimeError):
            print(f"Ошибка: {e}")
        else:
            traceback.print_exc()
        print()
input("Нажмите Enter для выхода...")

10. Коротко

  • Установить Python, выполнить py -m pip install pymupdf, для DjVu — поставить DjVuLibre.
  • Создать файлы pdf_shots.py и Скрины.bat в одной папке.
  • Перетащить книги на Скрины.bat.
  • Забрать картинки из папки <имя книги>_скрины и загрузить на хостинг трекера.
-Не получилось — напишите в этой теме, добавим решение в пункт 8.
Показать сообщения:    
Ответить на тему
В начало

Текущее время: 27-окт 10:38

Часовой пояс: UTC + 5



Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
▲