fix: HEIC vips fallback, DB pool exhaustion, auth session persistence

HEIC thumbnails:
- Switch fallback from ffmpeg to vips for HEIC files that pillow-heif
  rejects. ffmpeg decoded gain map tiles instead of the primary image,
  producing inverted/negative thumbnails. vips uses libheif's item
  references correctly and extracts the full-resolution primary image.

Database pool exhaustion:
- Add idle_in_transaction_session_timeout=60s so Postgres auto-kills
  leaked connections from disconnected thumbnail requests.
- Add pool_timeout=10 so new requests fail fast instead of hanging.
- Bump pool from 5+5 to 10+10 for thumbnail concurrency headroom.
- get_db rolls back on exception before closing.

Auth session persistence:
- Narrow 401 interceptor exclusion to only /auth/refresh and /auth/login
  (was excluding all /auth/* including /auth/me, preventing token refresh
  on boot).
- fetchMe only clears tokens on 401/403, not network errors.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
root
2026-04-13 15:25:55 +02:00
parent 2adaaf18a1
commit ecd8bbe61d
4 changed files with 39 additions and 18 deletions

View File

@@ -114,23 +114,27 @@ def process_heic_image(filepath: str) -> Image.Image:
img = img.convert('RGB')
return img
except Exception as e:
logger.warning(f"pillow-heif failed for {filepath}: {e} — trying ffmpeg")
logger.warning(f"pillow-heif failed for {filepath}: {e} — trying vips")
# ffmpeg fallback: decode HEIC to PNG in memory.
import subprocess
from io import BytesIO
# vips fallback: handles tiled Apple HEIC files (bursts, HDR gain
# maps, depth maps) that pillow-heif/libheif rejects due to too many
# auxiliary image references.
import subprocess, tempfile
try:
with tempfile.NamedTemporaryFile(suffix='.png', delete=False) as tmp:
tmp_path = tmp.name
result = subprocess.run(
['ffmpeg', '-i', filepath, '-frames:v', '1',
'-f', 'image2pipe', '-vcodec', 'png', 'pipe:1'],
capture_output=True, timeout=30, stdin=subprocess.DEVNULL,
['vips', 'heifload', filepath, tmp_path],
capture_output=True, timeout=60, stdin=subprocess.DEVNULL,
)
if result.returncode == 0 and result.stdout:
img = Image.open(BytesIO(result.stdout)).convert('RGB')
if result.returncode == 0:
img = Image.open(tmp_path).convert('RGB')
os.unlink(tmp_path)
return img
logger.error(f"ffmpeg HEIC decode failed for {filepath}: {result.stderr.decode()[-200:]}")
logger.error(f"vips HEIC decode failed for {filepath}: {result.stderr.decode()[-200:]}")
os.unlink(tmp_path)
except Exception as e2:
logger.error(f"ffmpeg fallback failed for {filepath}: {e2}")
logger.error(f"vips fallback failed for {filepath}: {e2}")
raise RuntimeError(f"Cannot decode HEIC: {filepath}")
def process_video_thumbnail(filepath: str) -> Image.Image: