Skip to content

fix(viewer): use writable cache dir for thumbnail generation#167

Merged
GeiserX merged 1 commit into
mainfrom
fix/media-thumbnails
May 24, 2026
Merged

fix(viewer): use writable cache dir for thumbnail generation#167
GeiserX merged 1 commit into
mainfrom
fix/media-thumbnails

Conversation

@GeiserX
Copy link
Copy Markdown
Owner

@GeiserX GeiserX commented May 24, 2026

Summary

  • Thumbnail generation was failing with [Errno 30] Read-only file system because the viewer container mounts the media volume as read-only (ro)
  • Thumbnails are now cached in a separate writable directory instead of {media_root}/.thumbs/
  • Priority: THUMBNAIL_CACHE_DIR env → {media_root}/.thumbs (if writable) → /tmp/telegram-archive-thumbs
  • This fixes both broken thumbnails in the Shared Media gallery AND the gallery appearing empty for channels/groups (same root cause)

Test plan

  • Deploy v7.10.11 to geiserback viewers
  • Open Shared Media gallery in a private chat → thumbnails load
  • Open Shared Media gallery in a channel → thumbnails load
  • Open Shared Media gallery in a group → thumbnails load
  • Verify no write attempts to the media volume (container logs clean)

Summary by CodeRabbit

  • New Features

    • Enhanced thumbnail caching: thumbnails now use a lazily-initialized, configurable cache directory (env var support and sensible fallbacks), improving performance and allowing storage outside the media root.
  • Chores

    • Version updated to 7.10.11.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 24, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 9400ab08-de48-4b04-82ab-d1c89f2bcfe7

📥 Commits

Reviewing files that changed from the base of the PR and between 76ae975 and f70bff3.

📒 Files selected for processing (4)
  • pyproject.toml
  • src/__init__.py
  • src/web/main.py
  • src/web/thumbnails.py
✅ Files skipped from review due to trivial changes (1)
  • src/init.py

📝 Walkthrough

Walkthrough

Adds configurable thumbnail cache directory selection (env-var, media-root subdirectory, or temp fallback), updates the thumbnail API to accept an optional cache directory parameter with validation, integrates lazy cache initialization in the web endpoint, and bumps the version to 7.10.11.

Changes

Version 7.10.11 Release with Configurable Thumbnail Cache

Layer / File(s) Summary
Cache directory resolution strategy
src/web/thumbnails.py
New resolve_cache_dir function implements precedence-based cache location selection: environment variable override, writable {media_root}/.thumbs directory, or /tmp/telegram-archive-thumbs fallback.
Thumbnail generation API updates
src/web/thumbnails.py
ensure_thumbnail signature extends to accept optional keyword-only cache_dir parameter; destination computation and containment checks updated to validate against resolved cache_dir when provided, otherwise against media_root/.thumbs. Module docstring updated to reflect configurable cache and thread-executor generation.
Endpoint integration with lazy cache initialization
src/web/main.py
Thumbnail serving endpoint adds module-level _thumb_cache_dir variable for lazy, per-process cache directory initialization. On first thumbnail request, resolve_cache_dir is called and cached; subsequent requests pass the resolved directory into ensure_thumbnail.
Version bump to 7.10.11
pyproject.toml, src/__init__.py
Package version incremented from 7.10.10 to 7.10.11 in both build metadata and runtime export.

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Description check ❓ Inconclusive The PR description covers the problem, solution, and priority order. However, it lacks completion of required template sections like Type of Change, Database Changes, and Testing checkboxes. Complete the required template sections: mark Type of Change (Bug fix), Database Changes (No changes), and Testing status (pass/fail for local checks).
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: resolving read-only file system issues by using a writable cache directory for thumbnail generation.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/media-thumbnails

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Copy Markdown

ghost commented May 24, 2026

🐳 Dev images published!

  • drumsergio/telegram-archive:dev
  • drumsergio/telegram-archive-viewer:dev

The dev/test instance will pick up these changes automatically (Portainer GitOps).

To test locally:

docker pull drumsergio/telegram-archive:dev
docker pull drumsergio/telegram-archive-viewer:dev

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/web/thumbnails.py`:
- Around line 40-52: The function currently returns a directory after mkdir(...)
which can succeed on an existing read-only dir and will raise if creating
THUMBNAIL_CACHE_DIR fails; update the code that handles env_dir and media_root
to catch exceptions from Path(env_dir).mkdir and, on success, verify the
directory is actually writable before returning (e.g., attempt to create and
remove a small temp file or use os.access(candidate, os.W_OK) with a real write
test); do the same for candidate (media_root / ".thumbs") inside the try/except
so that if creation or writability check fails the function falls back instead
of returning an unwritable path. Ensure references to env_dir, candidate,
media_root, Path, and mkdir are used to locate the changes.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 39c04e46-664e-454e-b46c-d67d8ebb178e

📥 Commits

Reviewing files that changed from the base of the PR and between e2c9ce5 and 76ae975.

📒 Files selected for processing (4)
  • pyproject.toml
  • src/__init__.py
  • src/web/main.py
  • src/web/thumbnails.py

Comment thread src/web/thumbnails.py
Comment on lines +40 to +52
env_dir = os.environ.get("THUMBNAIL_CACHE_DIR")
if env_dir:
p = Path(env_dir)
p.mkdir(parents=True, exist_ok=True)
return p

if media_root:
candidate = media_root / ".thumbs"
try:
candidate.mkdir(parents=True, exist_ok=True)
return candidate
except OSError:
pass
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Validate actual writability before returning a cache directory.

mkdir(..., exist_ok=True) can succeed for an existing read-only directory, so this may still return an unwritable {media_root}/.thumbs and reproduce the thumbnail failure. Also, if THUMBNAIL_CACHE_DIR creation fails, the function currently raises instead of falling back.

Suggested fix
+def _is_writable_dir(path: Path) -> bool:
+    try:
+        path.mkdir(parents=True, exist_ok=True)
+        probe = path / ".write_probe"
+        with probe.open("wb"):
+            pass
+        if probe.exists():
+            probe.unlink()
+        return True
+    except OSError:
+        return False
+
 def resolve_cache_dir(media_root: Path | None) -> Path:
@@
     env_dir = os.environ.get("THUMBNAIL_CACHE_DIR")
     if env_dir:
         p = Path(env_dir)
-        p.mkdir(parents=True, exist_ok=True)
-        return p
+        if _is_writable_dir(p):
+            return p
 
-    if media_root:
+    if media_root is not None:
         candidate = media_root / ".thumbs"
-        try:
-            candidate.mkdir(parents=True, exist_ok=True)
+        if _is_writable_dir(candidate):
             return candidate
-        except OSError:
-            pass
 
     p = Path(_DEFAULT_CACHE_DIR)
     p.mkdir(parents=True, exist_ok=True)
     return p
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/web/thumbnails.py` around lines 40 - 52, The function currently returns a
directory after mkdir(...) which can succeed on an existing read-only dir and
will raise if creating THUMBNAIL_CACHE_DIR fails; update the code that handles
env_dir and media_root to catch exceptions from Path(env_dir).mkdir and, on
success, verify the directory is actually writable before returning (e.g.,
attempt to create and remove a small temp file or use os.access(candidate,
os.W_OK) with a real write test); do the same for candidate (media_root /
".thumbs") inside the try/except so that if creation or writability check fails
the function falls back instead of returning an unwritable path. Ensure
references to env_dir, candidate, media_root, Path, and mkdir are used to locate
the changes.

The viewer container mounts the media volume read-only, causing
thumbnail generation to fail with EROFS. Thumbnails are now cached
in a separate writable directory (THUMBNAIL_CACHE_DIR env, or
/tmp/telegram-archive-thumbs as fallback).
@GeiserX GeiserX force-pushed the fix/media-thumbnails branch from 76ae975 to f70bff3 Compare May 24, 2026 14:39
@github-actions
Copy link
Copy Markdown

ghost commented May 24, 2026

🐳 Dev images published!

  • drumsergio/telegram-archive:dev
  • drumsergio/telegram-archive-viewer:dev

The dev/test instance will pick up these changes automatically (Portainer GitOps).

To test locally:

docker pull drumsergio/telegram-archive:dev
docker pull drumsergio/telegram-archive-viewer:dev

@codecov
Copy link
Copy Markdown

codecov Bot commented May 24, 2026

Codecov Report

❌ Patch coverage is 61.76471% with 13 lines in your changes missing coverage. Please review.
✅ Project coverage is 92.77%. Comparing base (c729979) to head (f70bff3).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
src/web/thumbnails.py 53.57% 13 Missing ⚠️

❌ Your patch check has failed because the patch coverage (61.76%) is below the target coverage (90.00%). You can increase the patch coverage or adjust the target coverage.

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main     #167      +/-   ##
==========================================
- Coverage   92.93%   92.77%   -0.16%     
==========================================
  Files          22       22              
  Lines        6466     6492      +26     
==========================================
+ Hits         6009     6023      +14     
- Misses        457      469      +12     
Files with missing lines Coverage Δ
src/__init__.py 100.00% <100.00%> (ø)
src/web/main.py 86.76% <100.00%> (+0.02%) ⬆️
src/web/thumbnails.py 82.19% <53.57%> (-15.81%) ⬇️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Comment thread src/web/thumbnails.py Dismissed
Comment thread src/web/thumbnails.py Dismissed
@GeiserX GeiserX merged commit fe98d57 into main May 24, 2026
8 of 10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants