Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0e106e6da6 | |||
| 68eb4a22b7 | |||
| 6c1a145577 | |||
| cc9ac6093f | |||
| fa60de79fd |
+1
-1
@@ -7,4 +7,4 @@ build
|
|||||||
.env
|
.env
|
||||||
*log
|
*log
|
||||||
*.md
|
*.md
|
||||||
uv.lock
|
src/api/uv.lock
|
||||||
|
|||||||
@@ -6,10 +6,6 @@ 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
|
||||||
|
|
||||||
@@ -44,9 +40,6 @@ 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"]
|
||||||
|
|||||||
@@ -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 © 2024</p>
|
<p>TheOnlyWayUp © 2026</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+20
-16
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
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
|
||||||
@@ -37,8 +36,6 @@ 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
|
||||||
@@ -161,13 +158,6 @@ 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
|
||||||
@@ -184,12 +174,26 @@ async def handle_download(
|
|||||||
story_zip = await fetch_story_content_zip(story_id, cookies)
|
story_zip = await fetch_story_content_zip(story_id, cookies)
|
||||||
archive = ZipFile(story_zip, "r")
|
archive = ZipFile(story_zip, "r")
|
||||||
|
|
||||||
part_trees: list[BeautifulSoup] = [
|
# Transform part metadata into an easily-indexable dictionary
|
||||||
clean_tree(
|
part_id_title_dictionary = {
|
||||||
part["title"], part["id"], archive.read(str(part["id"])).decode("utf-8")
|
str(part["id"]): part["title"] for part in metadata["parts"]
|
||||||
|
}
|
||||||
|
|
||||||
|
part_trees: list[BeautifulSoup] = []
|
||||||
|
|
||||||
|
for id in archive.namelist():
|
||||||
|
if (
|
||||||
|
id not in part_id_title_dictionary
|
||||||
|
): # If a part is deleted and the old story_zip is cached, this is needed to avoid a KeyError exception
|
||||||
|
continue
|
||||||
|
|
||||||
|
part_trees.append(
|
||||||
|
clean_tree(
|
||||||
|
part_id_title_dictionary[id],
|
||||||
|
id,
|
||||||
|
archive.read(id).decode("utf-8"),
|
||||||
|
)
|
||||||
)
|
)
|
||||||
for part in metadata["parts"]
|
|
||||||
]
|
|
||||||
|
|
||||||
images = (
|
images = (
|
||||||
[await fetch_tree_images(tree) for tree in part_trees]
|
[await fetch_tree_images(tree) for tree in part_trees]
|
||||||
@@ -225,7 +229,7 @@ async def handle_download(
|
|||||||
yield chunk
|
yield chunk
|
||||||
|
|
||||||
return StreamingResponse(
|
return StreamingResponse(
|
||||||
book_buffer if PDFS_ENABLED else iterfile(),
|
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
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
href="https://github.com/TheOnlyWayUp"
|
href="https://github.com/TheOnlyWayUp"
|
||||||
class="underline"
|
class="underline"
|
||||||
target="_blank">TheOnlyWayUp</a
|
target="_blank">TheOnlyWayUp</a
|
||||||
> © 2025
|
> © 2026
|
||||||
</p>
|
</p>
|
||||||
</aside>
|
</aside>
|
||||||
</footer>
|
</footer>
|
||||||
|
|||||||
@@ -44,7 +44,7 @@
|
|||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
<p>
|
<p>
|
||||||
Copyright © 2025 - All rights reserved by <a
|
Copyright © 2026 - All rights reserved by <a
|
||||||
href="https://rambhat.la"
|
href="https://rambhat.la"
|
||||||
class="link"
|
class="link"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
<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);
|
||||||
@@ -112,32 +110,29 @@
|
|||||||
>
|
>
|
||||||
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"
|
fill="none"
|
||||||
fill="none"
|
viewBox="0 0 24 24"
|
||||||
viewBox="0 0 24 24"
|
class="h-6 w-6 shrink-0 stroke-current"
|
||||||
class="h-6 w-6 shrink-0 stroke-current"
|
>
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
<div>
|
||||||
|
<p>
|
||||||
|
Donators get access to <span class="font-semibold">high-speed PDF Downloads</span>
|
||||||
|
</p>
|
||||||
|
<a href="https://buymeacoffee.com/theonlywayup" class="link" target="_blank"
|
||||||
|
>Donate now</a
|
||||||
>
|
>
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
|
|
||||||
></path>
|
|
||||||
</svg>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<p>
|
|
||||||
Donators get access to <span class="font-semibold">high-speed PDF Downloads</span>
|
|
||||||
</p>
|
|
||||||
<a href="https://buymeacoffee.com/theonlywayup" class="link" target="_blank"
|
|
||||||
>Donate now</a
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
</div>
|
||||||
<!-- <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"
|
||||||
@@ -159,13 +154,10 @@
|
|||||||
</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 - 📂 Less Errors, Throttled Downloads</li>
|
||||||
<li>12/24 - ⚡ Super-fast Downloads!</li>
|
|
||||||
<li>12/24 - 📑 PDF Downloads!</li>
|
|
||||||
{:else}
|
|
||||||
<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>
|
||||||
|
|
||||||
@@ -257,30 +249,26 @@
|
|||||||
href={url}
|
href={url}
|
||||||
onclick={() => (afterDownloadPage = true)}>Download</a
|
onclick={() => (afterDownloadPage = true)}>Download</a
|
||||||
>
|
>
|
||||||
</div>
|
|
||||||
|
|
||||||
{#if PDFS_ENABLED}
|
<!-- <label class="swap w-fit label mt-2">
|
||||||
<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 absolute left-0 text-gray-800">
|
<div class="swap-on">
|
||||||
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 absolute left-0 text-gray-800">
|
<div class="swap-off">
|
||||||
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"
|
<span class="label-text text-gray-800">Include Images (<strong>Slower Download</strong>)</span>
|
||||||
>Include Images (<strong>Slower Download</strong>)</span
|
<input
|
||||||
>
|
type="checkbox"
|
||||||
<input
|
class="checkbox-warning checkbox shadow-md"
|
||||||
type="checkbox"
|
bind:checked={downloadImages}
|
||||||
class="checkbox-warning checkbox shadow-md"
|
/>
|
||||||
bind:checked={downloadImages}
|
</label>
|
||||||
/>
|
</div>
|
||||||
</label>
|
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
|
|||||||
Reference in New Issue
Block a user