Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions codex/views/reader/page.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,8 +187,10 @@ def _get_page_image(self) -> tuple[bytes, str]:
# because ZipFile / RarFile / PDF backends aren't thread-safe.
with archive_cache.open(path) as cb:
page_image = cb.get_page_by_index(page, pdf_format="")
if not page_image:
page_image = b""
if page_image is None:
# comicbox's answer for an index past the last page.
detail = f"comic {pk} has no page {page}."
raise NotFound(detail=detail)

content_type = _PDF_MIME_TYPE if is_pdf else self.content_type
return page_image, content_type
Expand Down
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ dependencies = [
"asgiref~=3.12",
"bidict~=0.23",
"channels~=4.2",
"comicbox[pdf]~=5.2.1",
"comicbox[pdf]~=5.2.2",
"cryptography>=48.0.0",
"dateparser~=1.2",
"django-allauth[socialaccount]~=65.13",
Expand Down Expand Up @@ -76,7 +76,7 @@ dev = [
"infer-types~=1.0.0",
"neovim~=0.3",
"nplusone~=1.0",
"picopt~=6.5",
"picopt~=6.8.2",
"pypi-cleanup~=0.1.10",
"semver~=3.1.0",
"toml-cli~=0.7",
Expand Down
17 changes: 15 additions & 2 deletions tests/test_download_errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@
_TEST_PASSWORD: Final = "test-pw-hush-S106" # noqa: S105
_HTTP_OK: Final = 200
_HTTP_NOT_FOUND: Final = 404
# The example archive has one page, so index 1 is the first past the end.
_PAST_THE_END: Final = 1
_EACCES: Final = PermissionError(13, "Permission denied")
#: ``BOOK_AGE`` / ``PAGE_MAX_AGE``, the week these routes ask for.
_WEEK: Final = "max-age=604800"
Expand Down Expand Up @@ -152,8 +154,8 @@ def _download_url(self, index: int = 0) -> str:
def _pdf_url(self, index: int = 0) -> str:
return f"/c/{self.comics[index].pk}/book.pdf"

def _page_url(self, index: int = 0) -> str:
return f"/api/v4/comics/{self.comics[index].pk}/pages/0"
def _page_url(self, index: int = 0, page: int = 0) -> str:
return f"/api/v4/comics/{self.comics[index].pk}/pages/{page}"

def _collection_url(self) -> str:
return f"/api/v4/browse/series/{self.series.pk}/download/Ser.zip"
Expand Down Expand Up @@ -237,6 +239,17 @@ def test_an_unreadable_page_is_not_found(self) -> None:
response = self.client.get(self._page_url())
assert response.status_code == _HTTP_NOT_FOUND, response.status_code

def test_a_page_past_the_end_is_not_found(self) -> None:
"""
Comicbox answers ``None`` for an index past the last page.

That became an empty ``200 image/jpeg`` the route then told
clients to cache publicly for a week.
"""
response = self.client.get(self._page_url(page=_PAST_THE_END))
assert response.status_code == _HTTP_NOT_FOUND, response.status_code
assert _WEEK not in response.headers.get("Cache-Control", "")

def test_the_page_error_does_not_name_the_file(self) -> None:
"""The old detail string echoed the raw ``OSError``, path and all."""
self.paths[0].unlink()
Expand Down
22 changes: 11 additions & 11 deletions uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.