Compare commits

..
8 Commits
Author SHA1 Message Date
CaliBrain 8ea2fee0bb Fixing the title and book details from AA (#289)
Should fix #288
2025-10-04 14:44:10 -04:00
John Cocula 1c24312eb0 Update book_manager.py to fix #286 (#287)
Implement the fix mentioned in
https://github.com/calibrain/calibre-web-automated-book-downloader/issues/286

Note however that I have 0% success with downloads with 0.2.2 even with
this change.
2025-10-02 19:26:29 -04:00
CaliBrain 98e3a2f114 Add all supported format as default (#283) 2025-09-16 11:22:19 -04:00
CaliBrain cd16f09f2e Fix local download (#282) 2025-09-16 11:19:09 -04:00
CaliBrain 527c5d495d Fix formats in the HTML (read from config) (#279)
Fix #277
2025-09-09 08:15:44 -04:00
CaliBrain f5de2ab143 Fix AA extension parsing (#275)
Fix #274
2025-09-07 13:57:43 -04:00
RHDevandRyan Hults 4e5c9b788f Display book covers at full height (#266)
# Why
Book covers in the UI are currently cut off on the top and bottom,
making it hard to see.

# How
doubled the height of the book cover image div so the covers are not cut
off. I found that setting it to a specific size (rather than `h-full`)
resulted in better handling of small images and made for a more
consistent look.

# Before
<img width="488" height="520" alt="Screenshot from 2025-09-02 11-44-49"
src="https://github.com/user-attachments/assets/cf94e5f7-3981-40b6-a148-2a847f565c41"
/>
<img width="488" height="520" alt="Screenshot from 2025-09-02 11-45-06"
src="https://github.com/user-attachments/assets/5849eb34-57e8-4c36-af26-cb2f9647605f"
/>



# After
<img width="488" height="520" alt="Screenshot from 2025-09-02 11-41-41"
src="https://github.com/user-attachments/assets/6ffdde1f-ba36-4253-8092-12afe1c8f84e"
/>
<img width="488" height="520" alt="Screenshot from 2025-09-02 11-44-38"
src="https://github.com/user-attachments/assets/e44b57b7-ee03-4e8b-a186-444e8a5bf5aa"
/>

---------

Co-authored-by: Ryan Hults <contact@ryanthults.com>
2025-09-02 13:13:10 -04:00
CaliBrain 199d8453eb Adding Release version (#263) 2025-08-30 03:10:15 -04:00
12 changed files with 117 additions and 29 deletions
@@ -70,6 +70,7 @@ jobs:
push: ${{ github.event_name != 'pull_request' }}
build-args: |
BUILD_VERSION=${{ steps.date.outputs.date }}-${{ github.sha }}
RELEASE_VERSION=${{ github.ref_name }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
+2
View File
@@ -4,6 +4,8 @@ FROM python:3.10-slim AS base
# Add build argument for version
ARG BUILD_VERSION
ENV BUILD_VERSION=${BUILD_VERSION}
ARG RELEASE_VERSION
ENV RELEASE_VERSION=${RELEASE_VERSION}
# Set shell to bash with pipefail option
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
+11 -3
View File
@@ -12,8 +12,8 @@ from flask import url_for as flask_url_for
import typing
from logger import setup_logger
from config import _SUPPORTED_BOOK_LANGUAGE, BOOK_LANGUAGE
from env import FLASK_HOST, FLASK_PORT, APP_ENV, CWA_DB_PATH, DEBUG, USING_EXTERNAL_BYPASSER
from config import _SUPPORTED_BOOK_LANGUAGE, BOOK_LANGUAGE, SUPPORTED_FORMATS
from env import FLASK_HOST, FLASK_PORT, APP_ENV, CWA_DB_PATH, DEBUG, USING_EXTERNAL_BYPASSER, BUILD_VERSION, RELEASE_VERSION
import backend
from models import SearchFilters
@@ -103,7 +103,15 @@ def index() -> str:
"""
Render main page with search and status table.
"""
return render_template('index.html', book_languages=_SUPPORTED_BOOK_LANGUAGE, default_language=BOOK_LANGUAGE, debug=DEBUG)
return render_template('index.html',
book_languages=_SUPPORTED_BOOK_LANGUAGE,
default_language=BOOK_LANGUAGE,
supported_formats=SUPPORTED_FORMATS,
debug=DEBUG,
build_version=BUILD_VERSION,
release_version=RELEASE_VERSION,
app_env=APP_ENV
)
+6
View File
@@ -81,6 +81,12 @@ def queue_status() -> Dict[str, Dict[str, Any]]:
Dict: Queue status organized by status type
"""
status = book_queue.get_status()
for _, books in status.items():
for _, book_info in books.items():
if book_info.download_path:
if not os.path.exists(book_info.download_path):
book_info.download_path = None
# Convert Enum keys to strings and properly format the response
return {
status_type.value: books
+36 -7
View File
@@ -10,7 +10,7 @@ from bs4 import BeautifulSoup, Tag, NavigableString, ResultSet
import downloader
from logger import setup_logger
from config import SUPPORTED_FORMATS, BOOK_LANGUAGE, AA_BASE_URL
from env import AA_DONATOR_KEY, USE_CF_BYPASS, PRIORITIZE_WELIB
from env import AA_DONATOR_KEY, USE_CF_BYPASS, PRIORITIZE_WELIB, ALLOW_USE_WELIB
from models import BookInfo, SearchFilters
logger = setup_logger(__name__)
@@ -169,8 +169,6 @@ def _parse_book_info_page(soup: BeautifulSoup, book_id: str) -> BookInfo:
data = soup.find_all("div", {"class": "main-inner"})[0].find_next("div")
divs = list(data.children)
format = divs[13].text.split(" · ")[-6].strip().lower()
size = divs[13].text.split(" · ")[-5].strip().lower()
every_url = soup.find_all("a")
slow_urls_no_waitlist = set()
@@ -224,20 +222,49 @@ def _parse_book_info_page(soup: BeautifulSoup, book_id: str) -> BookInfo:
# Remove empty urls
urls = [url for url in urls if url != ""]
# Filter out divs that are not text
original_divs = divs
divs = [div.text.strip() for div in divs if div.text.strip() != ""]
separator_index = 6
for i, div in enumerate(divs):
if "·" in div.strip():
separator_index = i
break
_details = divs[separator_index].lower().split(" · ")
format = ""
size = ""
for f in _details:
if format == "" and f.strip().lower() in SUPPORTED_FORMATS:
format = f.strip().lower()
if size == "" and any(u in f.strip().lower() for u in ["mb", "kb", "gb"]):
size = f.strip().lower()
if format == "" or size == "":
for f in _details:
if f == "" and not " " in f.strip().lower():
format = f.strip().lower()
if size == "" and "." in f.strip().lower():
size = f.strip().lower()
book_title = divs[separator_index-3].strip("🔍")
# Extract basic information
book_info = BookInfo(
id=book_id,
preview=preview,
title=divs[7].next.strip(),
publisher=divs[11].text.strip(),
author=divs[9].text.strip(),
title=book_title,
publisher=divs[separator_index-1],
author=divs[separator_index-2],
format=format,
size=size,
download_urls=urls,
)
# Extract additional metadata
info = _extract_book_metadata(divs[-6])
info = _extract_book_metadata(original_divs[-6])
book_info.info = info
# Set language and year from metadata if available
@@ -249,6 +276,8 @@ def _parse_book_info_page(soup: BeautifulSoup, book_id: str) -> BookInfo:
return book_info
def _get_download_urls_from_welib(book_id: str) -> set[str]:
if ALLOW_USE_WELIB == False:
return set()
"""Get download urls from welib.org."""
url = f"https://welib.org/md5/{book_id}"
logger.info(f"Getting download urls from welib.org for {book_id}. While this uses the bypasser, it will not start downloading them yet.")
+1
View File
@@ -20,6 +20,7 @@ set -e
# Print build version
echo "Build version: $BUILD_VERSION"
echo "Release version: $RELEASE_VERSION"
# Configure timezone
if [ "$TZ" ]; then
+7 -1
View File
@@ -26,7 +26,13 @@ _CUSTOM_SCRIPT = os.getenv("CUSTOM_SCRIPT", "").strip()
FLASK_HOST = os.getenv("FLASK_HOST", "0.0.0.0")
FLASK_PORT = int(os.getenv("FLASK_PORT", "8084"))
DEBUG = string_to_bool(os.getenv("DEBUG", "false"))
APP_ENV = os.getenv("APP_ENV", "N/A").lower()
PRIORITIZE_WELIB = string_to_bool(os.getenv("PRIORITIZE_WELIB", "false"))
ALLOW_USE_WELIB = string_to_bool(os.getenv("ALLOW_USE_WELIB", "true"))
# Version information from Docker build
BUILD_VERSION = os.getenv("BUILD_VERSION", "N/A")
RELEASE_VERSION = os.getenv("RELEASE_VERSION", "N/A")
# If debug is true, we want to log everything
if DEBUG:
@@ -41,7 +47,7 @@ DOCKERMODE = string_to_bool(os.getenv("DOCKERMODE", "false"))
_CUSTOM_DNS = os.getenv("CUSTOM_DNS", "").strip()
USE_DOH = string_to_bool(os.getenv("USE_DOH", "false"))
BYPASS_RELEASE_INACTIVE_MIN = int(os.getenv("BYPASS_RELEASE_INACTIVE_MIN", "5"))
APP_ENV = os.getenv("APP_ENV", "prod").lower()
# Logging settings
LOG_FILE = LOG_DIR / "cwa-book-downloader.log"
+5 -5
View File
@@ -3,7 +3,7 @@
# Set up log paths
LOG_ROOT=${LOG_ROOT:-"/var/log"}
LOG_DIR="$LOG_ROOT/cwa-book-downloader"
OUTPUT_FILE_NAME="cwa-book-downloader-debug_BUILD-${BUILD_VERSION:-local}_$(date +%Y%m%d-%H%M%S)"
OUTPUT_FILE_NAME="cwa-book-downloader-debug_BUILD-${BUILD_VERSION:-local}_RELEASE-${RELEASE_VERSION:-NA}_$(date +%Y%m%d-%H%M%S)"
OUTPUT_FILE="/tmp/$OUTPUT_FILE_NAME.zip"
# Create LOG_DIR if it doesn't exist
@@ -125,16 +125,16 @@ fi
env | grep -v -E "(AA_DONATOR_KEY)" | sort > "$LOG_DIR/environment.txt"
echo "--- HTTPBin ---" > $LOG_DIR/network_info.txt
pyrequests https://httpbin.org/get >> $LOG_DIR/network_info.txt
curl -s https://httpbin.org/get >> $LOG_DIR/network_info.txt
ehco ""
echo "--- HowsMySSL ---" >> $LOG_DIR/network_info.txt
pyrequests https://www.howsmyssl.com/a/check >> $LOG_DIR/network_info.txt
curl -s https://www.howsmyssl.com/a/check >> $LOG_DIR/network_info.txt
ehco ""
echo "--- IPInfo ---" >> $LOG_DIR/network_info.txt
pyrequests https://ipinfo.io >> $LOG_DIR/network_info.txt
curl -s https://ipinfo.io >> $LOG_DIR/network_info.txt
ehco ""
echo "--- Cloudflare Trace ---" >> $LOG_DIR/network_info.txt
pyrequests https://1.1.1.1/cdn-cgi/trace >> $LOG_DIR/network_info.txt
curl -s https://1.1.1.1/cdn-cgi/trace >> $LOG_DIR/network_info.txt
# Create the zip file directly from LOG_DIR
ln -s "$LOG_DIR" /tmp/$OUTPUT_FILE_NAME
+1
View File
@@ -83,6 +83,7 @@ Note that if using TOR, the TZ will be calculated automatically based on IP.
| `AA_DONATOR_KEY` | Optional Donator key for Anna's Archive fast download API | `` |
| `USE_BOOK_TITLE` | Use book title as filename instead of ID | `false` |
| `PRIORITIZE_WELIB` | When downloading, download from WELIB first instead of AA | `false` |
| `ALLOW_USE_WELIB` | Allow usage of welib for downloading books if found there | `true` |
If you change `BOOK_LANGUAGE`, you can add multiple comma separated languages, such as `en,fr,ru` etc.
+8 -4
View File
@@ -94,8 +94,8 @@
// ---- Cards ----
function renderCard(book) {
const cover = book.preview ? `<img src="${utils.e(book.preview)}" alt="Cover" class="w-full h-44 object-cover rounded">` :
`<div class="w-full h-44 rounded flex items-center justify-center opacity-70" style="background: var(--bg-soft)">No Cover</div>`;
const cover = book.preview ? `<img src="${utils.e(book.preview)}" alt="Cover" class="w-full h-88 object-cover rounded">` :
`<div class="w-full h-88 rounded flex items-center justify-center opacity-70" style="background: var(--bg-soft)">No Cover</div>`;
const html = `
<article class="rounded border p-3 flex flex-col gap-3" style="border-color: var(--border-muted); background: var(--bg-soft)">
@@ -172,7 +172,7 @@
}
},
tpl(book) {
const cover = book.preview ? `<img src="${utils.e(book.preview)}" alt="Cover" class="w-full h-56 object-cover rounded">` : '';
const cover = book.preview ? `<img src="${utils.e(book.preview)}" alt="Cover" class="w-full h-88 object-cover rounded">` : '';
const infoList = book.info ? Object.entries(book.info).map(([k, v]) => `<li><strong>${utils.e(k)}:</strong> ${utils.e((v||[]).join
? v.join(', ') : v)}</li>`).join('') : '';
return `
@@ -229,6 +229,10 @@
for (const [name, items] of Object.entries(data || {})) {
if (!items || Object.keys(items).length === 0) continue;
const rows = Object.values(items).map((b) => {
const titleText = utils.e(b.title) || '-';
const maybeLinkedTitle = b.download_path
? `<a href="/request/api/localdownload?id=${encodeURIComponent(b.id)}" class="text-blue-600 hover:underline">${titleText}</a>`
: titleText;
const actions = (name === 'queued' || name === 'downloading')
? `<button class="px-2 py-1 rounded border text-xs" data-cancel="${utils.e(b.id)}" style="border-color: var(--border-muted);">Cancel</button>`
: '';
@@ -236,7 +240,7 @@
? `<div class="h-2 bg-black/10 rounded overflow-hidden"><div class="h-2 bg-blue-600" style="width:${Math.round(b.progress)}%"></div></div>`
: '';
return `<li class="p-3 rounded border flex flex-col gap-2" style="border-color: var(--border-muted); background: var(--bg-soft)">
<div class="text-sm"><span class="opacity-70">${utils.e(name)}</span> • <strong>${utils.e(b.title || '-') }</strong></div>
<div class="text-sm"><span class="opacity-70">${utils.e(name)}</span> • <strong>${maybeLinkedTitle}</strong></div>
${progress}
<div class="flex items-center gap-2">${actions}</div>
</li>`;
+38 -9
View File
@@ -132,14 +132,38 @@
<div class="md:col-span-2 lg:col-span-3">
<label class="block text-sm mb-1 opacity-80">Formats</label>
<div class="flex flex-wrap gap-3 text-sm">
<label class="inline-flex items-center gap-2"><input type="checkbox" id="format-pdf" value="pdf"> PDF</label>
<label class="inline-flex items-center gap-2"><input type="checkbox" id="format-epub" value="epub" checked> EPUB</label>
<label class="inline-flex items-center gap-2"><input type="checkbox" id="format-mobi" value="mobi" checked> MOBI</label>
<label class="inline-flex items-center gap-2"><input type="checkbox" id="format-azw3" value="azw3" checked> AZW3</label>
<label class="inline-flex items-center gap-2"><input type="checkbox" id="format-fb2" value="fb2" checked> FB2</label>
<label class="inline-flex items-center gap-2"><input type="checkbox" id="format-djvu" value="djvu" checked> DJVU</label>
<label class="inline-flex items-center gap-2"><input type="checkbox" id="format-cbz" value="cbz" checked> CBZ</label>
<label class="inline-flex items-center gap-2"><input type="checkbox" id="format-cbr" value="cbr" checked> CBR</label>
<label class="inline-flex items-center gap-2 {{ 'opacity-50 cursor-not-allowed' if 'pdf' not in supported_formats else '' }}">
<input type="checkbox" id="format-pdf" value="pdf" {% if 'pdf' not in supported_formats %}disabled{% endif %}>
PDF
</label>
<label class="inline-flex items-center gap-2 {{ 'opacity-50 cursor-not-allowed' if 'epub' not in supported_formats else '' }}">
<input type="checkbox" id="format-epub" value="epub" {% if 'epub' in supported_formats %}checked{% endif %} {% if 'epub' not in supported_formats %}disabled{% endif %}>
EPUB
</label>
<label class="inline-flex items-center gap-2 {{ 'opacity-50 cursor-not-allowed' if 'mobi' not in supported_formats else '' }}">
<input type="checkbox" id="format-mobi" value="mobi" {% if 'mobi' in supported_formats %}checked{% endif %} {% if 'mobi' not in supported_formats %}disabled{% endif %}>
MOBI
</label>
<label class="inline-flex items-center gap-2 {{ 'opacity-50 cursor-not-allowed' if 'azw3' not in supported_formats else '' }}">
<input type="checkbox" id="format-azw3" value="azw3" {% if 'azw3' in supported_formats %}checked{% endif %} {% if 'azw3' not in supported_formats %}disabled{% endif %}>
AZW3
</label>
<label class="inline-flex items-center gap-2 {{ 'opacity-50 cursor-not-allowed' if 'fb2' not in supported_formats else '' }}">
<input type="checkbox" id="format-fb2" value="fb2" {% if 'fb2' in supported_formats %}checked{% endif %} {% if 'fb2' not in supported_formats %}disabled{% endif %}>
FB2
</label>
<label class="inline-flex items-center gap-2 {{ 'opacity-50 cursor-not-allowed' if 'djvu' not in supported_formats else '' }}">
<input type="checkbox" id="format-djvu" value="djvu" {% if 'djvu' in supported_formats %}checked{% endif %} {% if 'djvu' not in supported_formats %}disabled{% endif %}>
DJVU
</label>
<label class="inline-flex items-center gap-2 {{ 'opacity-50 cursor-not-allowed' if 'cbz' not in supported_formats else '' }}">
<input type="checkbox" id="format-cbz" value="cbz" {% if 'cbz' in supported_formats %}checked{% endif %} {% if 'cbz' not in supported_formats %}disabled{% endif %}>
CBZ
</label>
<label class="inline-flex items-center gap-2 {{ 'opacity-50 cursor-not-allowed' if 'cbr' not in supported_formats else '' }}">
<input type="checkbox" id="format-cbr" value="cbr" {% if 'cbr' in supported_formats %}checked{% endif %} {% if 'cbr' not in supported_formats %}disabled{% endif %}>
CBR
</label>
</div>
</div>
<div class="md:col-span-2 lg:col-span-3 flex justify-end">
@@ -193,7 +217,12 @@
<footer class="mt-10 border-t pt-6 pb-10" style="border-color: var(--border-muted); background: var(--footer-bg);">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 flex items-center justify-between">
<p class="text-sm opacity-80">Calibre Web Book Downloader</p>
<div>
<p class="text-sm opacity-80">Calibre Web Book Downloader</p>
<p class="text-xs opacity-60 mt-1">
Build: {{ build_version }} • Release: {{ release_version }} • Env: {{ app_env }}
</p>
</div>
<a href="https://github.com/calibrain/calibre-web-automated-book-downloader" class="opacity-80 hover:opacity-100" aria-label="GitHub">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" class="w-6 h-6">
<path d="M8 0C3.58 0 0 3.58 0 8a8 8 0 005.47 7.59c.4.07.55-.17.55-.38
+1
View File
@@ -14,6 +14,7 @@ set -e
echo "[*] Running tor script..."
echo "Build version: $BUILD_VERSION"
echo "Release version: $RELEASE_VERSION"
echo "[*] Installing Tor and dependencies..."
echo "[*] Writing Tor transparent proxy config..."