5 Commits

Author SHA1 Message Date
Aron BenDaniel 0e106e6da6 Update copyright year to 2026 2026-01-08 01:12:06 +05:30
Aron BenDaniel 68eb4a22b7 fix(api): Restructure archive parsing to iterate through archive items 2026-01-07 00:48:15 +05:30
Aron BenDaniel 6c1a145577 docker: Fix .dockerignore uv.lock entry 2025-10-31 05:49:08 +05:30
Dhanush R cc9ac6093f feat(api): Remove dependency on exiftool (#82) 2025-10-30 16:11:16 +05:30
Aaron BenDaniel fa60de79fd Update README.md copyright year
The future is now.
2025-10-29 01:44:54 +05:30
7 changed files with 61 additions and 76 deletions
+1 -1
View File
@@ -7,4 +7,4 @@ build
.env .env
*log *log
*.md *.md
uv.lock src/api/uv.lock
-7
View File
@@ -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"]
+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 © 2024</p> <p>TheOnlyWayUp © 2026</p>
</div> </div>
+20 -16
View File
@@ -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
+1 -1
View File
@@ -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>
+1 -1
View File
@@ -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"
+37 -49
View File
@@ -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}