5 Commits

Author SHA1 Message Date
Aron BenDaniel f20dfa2017 feat(api): Made PDFs-enabled check earlier 2025-10-30 13:43:12 -04:00
Aron BenDaniel 265799907d docker: Fix merge conflict with #82 2025-10-30 13:38:13 -04:00
AaronBenDaniel 2956399b4b fix(api): Change error message capitalization 2025-10-29 18:02:42 +05:30
AaronBenDaniel d372020bac fix: Change name of flag to enable PDF downloads 2025-10-29 18:02:41 +05:30
AaronBenDaniel c2104ee514 feat: Control features with build arg 2025-10-29 18:02:37 +05:30
5 changed files with 87 additions and 56 deletions
+10 -11
View File
@@ -1,11 +1,10 @@
**/__pycache__/ __pycache__
**/*.ipynb *ipynb
**/build/ build
.idea/ .idea
.vscode/ .vscode
**/.venv/ .venv
**/.env .env
**/.env_template *log
**/*.log *.md
**/*.md uv.lock
src/api/uv.lock
+16 -6
View File
@@ -1,4 +1,4 @@
FROM node:20-alpine FROM node:20
WORKDIR /build WORKDIR /build
COPY src/frontend/package*.json . COPY src/frontend/package*.json .
@@ -6,6 +6,10 @@ RUN rm -rf node_modules
RUN rm -rf build RUN rm -rf build
RUN npm install RUN npm install
COPY src/frontend/. . COPY src/frontend/. .
ARG pdfs=false
ENV VITE_ENABLE_PDFS=$pdfs
RUN npm run build RUN npm run build
# Thanks https://stackoverflow.com/q/76988450 # Thanks https://stackoverflow.com/q/76988450
@@ -13,12 +17,15 @@ FROM python:3.13-slim
WORKDIR /app WORKDIR /app
RUN apt update && \ COPY --from=nobodyxu/apt-fast:latest-debian-buster-slim /usr/local/ /usr/local/
apt install -y git build-essential python3.13-dev libglib2.0-0 libpango-1.0-0 libpangoft2-1.0-0 && \
apt clean && \ RUN apt update
rm -rf /var/lib/apt/lists/* RUN apt install -y aria2
RUN apt-fast install -y git build-essential python3.13-dev libgobject-2.0 libpango-1.0 libpangoft2-1.0
# aiohttp-client-cache depends on multipart, which requires python3.13-dev to build successfully on 3.13 # aiohttp-client-cache depends on multipart, which requires python3.13-dev to build successfully on 3.13
# weasyprint depends on libgoject, libpango, and libpangoft2 # weasyprint depends on libgoject, libpango, and libpangoft2
RUN rm -rf /var/lib/apt/lists/*
# https://github.com/TheOnlyWayUp/WattpadDownloader/pull/82#discussion_r2470358950 # https://github.com/TheOnlyWayUp/WattpadDownloader/pull/82#discussion_r2470358950
@@ -29,7 +36,7 @@ WORKDIR /app
COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/ COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/
COPY src/api/pyproject.toml /app COPY src/api/pyproject.toml /app
RUN uv sync && uv cache clean RUN uv sync
COPY src/api/ /app COPY src/api/ /app
COPY --from=0 /build/build /app/src/build COPY --from=0 /build/build /app/src/build
@@ -37,6 +44,9 @@ RUN ln -s /app/src/pdf/fonts /tmp/fonts
WORKDIR /app/src WORKDIR /app/src
ARG pdfs=false
ENV VITE_ENABLE_PDFS=$pdfs
EXPOSE 80 EXPOSE 80
CMD [ "uv", "run", "main.py"] CMD [ "uv", "run", "main.py"]
+1 -1
View File
@@ -53,5 +53,5 @@ My thanks to [aerkalov/ebooklib](https://github.com/aerkalov/ebooklib) for a fas
--- ---
<div align="center"> <div align="center">
<p>TheOnlyWayUp © 2025</p> <p>TheOnlyWayUp © 2024</p>
</div> </div>
+11 -1
View File
@@ -2,6 +2,7 @@
import asyncio import asyncio
from enum import Enum from enum import Enum
from os import getenv
from pathlib import Path from pathlib import Path
from typing import Optional from typing import Optional
from zipfile import ZipFile from zipfile import ZipFile
@@ -36,6 +37,8 @@ from create_book.parser import clean_tree, fetch_tree_images
app = FastAPI() app = FastAPI()
BUILD_PATH = Path(__file__).parent / "build" BUILD_PATH = Path(__file__).parent / "build"
PDFS_ENABLED = True if getenv("VITE_ENABLE_PDFS") == "true" else False
class RequestCancelledMiddleware: class RequestCancelledMiddleware:
# Thanks https://github.com/fastapi/fastapi/discussions/11360#discussion-6427734 # Thanks https://github.com/fastapi/fastapi/discussions/11360#discussion-6427734
@@ -158,6 +161,13 @@ async def handle_download(
else: else:
cookies = None cookies = None
if format == DownloadFormat.pdf and not PDFS_ENABLED:
logger.error("PDF Downloads not enabled.")
return HTMLResponse(
status_code=403,
content='PDF Downloads have been disabled by the server administrator. Support is available on the <a href="https://discord.gg/P9RHC4KCwd" target="_blank">Discord</a>',
)
match mode: match mode:
case DownloadMode.story: case DownloadMode.story:
story_id = download_id story_id = download_id
@@ -215,7 +225,7 @@ async def handle_download(
yield chunk yield chunk
return StreamingResponse( return StreamingResponse(
iterfile(), book_buffer if PDFS_ENABLED else iterfile(),
media_type=media_type, media_type=media_type,
headers={ headers={
"Content-Disposition": f'attachment; filename="{slugify(metadata["title"])}_{story_id}{"_images" if download_images else ""}.{format.value}"', # Thanks https://stackoverflow.com/a/72729058 "Content-Disposition": f'attachment; filename="{slugify(metadata["title"])}_{story_id}{"_images" if download_images else ""}.{format.value}"', # Thanks https://stackoverflow.com/a/72729058
+20 -8
View File
@@ -1,4 +1,6 @@
<script> <script>
const PDFS_ENABLED = import.meta.env.VITE_ENABLE_PDFS === "true";
let downloadImages = $state(false); let downloadImages = $state(false);
let downloadAsPdf = $state(false); // 0 = epub, 1 = pdf let downloadAsPdf = $state(false); // 0 = epub, 1 = pdf
let isPaidStory = $state(false); let isPaidStory = $state(false);
@@ -110,6 +112,7 @@
> >
WP Downloader WP Downloader
</h1> </h1>
{#if !PDFS_ENABLED}
<div role="alert" class="alert mt-10 max-w-md break-words bg-green-200"> <div role="alert" class="alert mt-10 max-w-md break-words bg-green-200">
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
@@ -124,6 +127,7 @@
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
></path> ></path>
</svg> </svg>
<div> <div>
<p> <p>
Donators get access to <span class="font-semibold">high-speed PDF Downloads</span> Donators get access to <span class="font-semibold">high-speed PDF Downloads</span>
@@ -133,6 +137,7 @@
> >
</div> </div>
</div> </div>
{/if}
<!-- <div role="alert" class="alert bg-cyan-300 mt-5"> <!-- <div role="alert" class="alert bg-cyan-300 mt-5">
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
@@ -154,10 +159,13 @@
</p> </p>
<ul class="list list-inside pt-4 text-xl"> <ul class="list list-inside pt-4 text-xl">
<!-- TODO: 'max-lg: hidden' to hide on screen sizes smaller than lg. I'll do this when I figure out how to make this show up _below_ the card on smaller screen sizes. --> <!-- TODO: 'max-lg: hidden' to hide on screen sizes smaller than lg. I'll do this when I figure out how to make this show up _below_ the card on smaller screen sizes. -->
<!-- <li>12/24 - ⚡ Super-fast Downloads!</li>
<li>12/24 - 📑 PDF Downloads!</li> -->
<li>05/25 - ⚖️ Legal Compliance</li> <li>05/25 - ⚖️ Legal Compliance</li>
{#if PDFS_ENABLED}
<li>12/24 - ⚡ Super-fast Downloads!</li>
<li>12/24 - 📑 PDF Downloads!</li>
{:else}
<li>12/24 - 📂 Less Errors, Throttled Downloads</li> <li>12/24 - 📂 Less Errors, Throttled Downloads</li>
{/if}
<li>11/24 - 🔗 Paste Links!</li> <li>11/24 - 🔗 Paste Links!</li>
<li>11/24 - 📨 Send to Kindle Support!</li> <li>11/24 - 📨 Send to Kindle Support!</li>
@@ -249,26 +257,30 @@
href={url} href={url}
onclick={() => (afterDownloadPage = true)}>Download</a onclick={() => (afterDownloadPage = true)}>Download</a
> >
</div>
<!-- <label class="swap w-fit label mt-2"> {#if PDFS_ENABLED}
<label class="swap w-fit label mt-2 pb-2">
<input type="checkbox" bind:checked={downloadAsPdf} /> <input type="checkbox" bind:checked={downloadAsPdf} />
<div class="swap-on"> <div class="swap-on absolute left-0 text-gray-800">
Downloading as <span class=" underline text-bold">PDF</span> (Click) Downloading as <span class=" underline text-bold">PDF</span> (Click)
</div> </div>
<div class="swap-off"> <div class="swap-off absolute left-0 text-gray-800">
Downloading as <span class=" underline text-bold">EPUB</span> (Click) Downloading as <span class=" underline text-bold">EPUB</span> (Click)
</div> </div>
</label> --> </label>
{/if}
<label class="label cursor-pointer"> <label class="label cursor-pointer">
<span class="label-text text-gray-800">Include Images (<strong>Slower Download</strong>)</span> <span class="label-text text-gray-800"
>Include Images (<strong>Slower Download</strong>)</span
>
<input <input
type="checkbox" type="checkbox"
class="checkbox-warning checkbox shadow-md" class="checkbox-warning checkbox shadow-md"
bind:checked={downloadImages} bind:checked={downloadImages}
/> />
</label> </label>
</div>
</form> </form>
</div> </div>
{:else} {:else}