juns commited on
Commit
7962d2c
·
1 Parent(s): b2f7e8c

Fix Railway deployment: encoding bug, healthcheck, deps pinning, Dockerfile optimization

Browse files

- worker/index.js: Remove Content-Encoding forwarding (CF Workers auto-decompress)
- main.py: Add /health endpoint, make Playwright pre-warm non-fatal
- railway.toml: Add [deploy] with healthcheck, restart policy
- requirements.txt: Pin all dependency versions
- Dockerfile: Use playwright install --with-deps, optimize layer caching

Files changed (5) hide show
  1. Dockerfile +8 -23
  2. main.py +11 -2
  3. railway.toml +6 -0
  4. requirements.txt +6 -6
  5. worker/index.js +7 -7
Dockerfile CHANGED
@@ -1,37 +1,22 @@
1
  FROM python:3.11-slim
2
 
3
- # Install system dependencies for Playwright Chromium
4
  RUN apt-get update && apt-get install -y --no-install-recommends \
5
- wget \
6
  ffmpeg \
7
- libnss3 \
8
- libnspr4 \
9
- libatk1.0-0 \
10
- libatk-bridge2.0-0 \
11
- libcups2 \
12
- libdrm2 \
13
- libxkbcommon0 \
14
- libxcomposite1 \
15
- libxdamage1 \
16
- libxfixes3 \
17
- libxrandr2 \
18
- libgbm1 \
19
- libpango-1.0-0 \
20
- libcairo2 \
21
- libasound2t64 \
22
- libatspi2.0-0 \
23
- libwayland-client0 \
24
  && rm -rf /var/lib/apt/lists/*
25
 
26
  WORKDIR /app
27
 
 
28
  COPY requirements.txt .
29
  RUN pip install --no-cache-dir -r requirements.txt
30
 
31
- # Install Playwright Chromium browser
32
- RUN playwright install chromium
 
33
 
 
34
  COPY . .
35
 
36
- # Railway provides PORT env var
37
- CMD uvicorn main:app --host 0.0.0.0 --port ${PORT:-8000}
 
1
  FROM python:3.11-slim
2
 
3
+ # Install ffmpeg (not a Playwright dep, needed for Instagram audio extraction)
4
  RUN apt-get update && apt-get install -y --no-install-recommends \
 
5
  ffmpeg \
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6
  && rm -rf /var/lib/apt/lists/*
7
 
8
  WORKDIR /app
9
 
10
+ # Layer 1: Python dependencies (cached unless requirements.txt changes)
11
  COPY requirements.txt .
12
  RUN pip install --no-cache-dir -r requirements.txt
13
 
14
+ # Layer 2: Playwright Chromium + all system deps (auto-installs libnss3, libatk, etc.)
15
+ # Using --with-deps eliminates manual apt-get for ~15 Chromium libraries
16
+ RUN playwright install --with-deps chromium
17
 
18
+ # Layer 3: Application code (changes most frequently)
19
  COPY . .
20
 
21
+ # Railway provides PORT env var; default to 8000 for local dev
22
+ CMD ["sh", "-c", "uvicorn main:app --host 0.0.0.0 --port ${PORT:-8000}"]
main.py CHANGED
@@ -413,8 +413,11 @@ def _pw_init_browser():
413
  return _ig_browser
414
 
415
 
416
- # Pre-warm browser at import time
417
- _pw_executor.submit(_pw_init_browser)
 
 
 
418
 
419
 
420
  def _pw_extract_embed(shortcode):
@@ -884,3 +887,9 @@ async def submit_feedback(request: FeedbackRequest):
884
  @app.get("/")
885
  async def root():
886
  return FileResponse("static/index.html")
 
 
 
 
 
 
 
413
  return _ig_browser
414
 
415
 
416
+ # Pre-warm browser at import time (fire-and-forget; failure is non-fatal)
417
+ try:
418
+ _pw_executor.submit(_pw_init_browser)
419
+ except Exception:
420
+ logger.warning("[instagram] Failed to submit browser pre-warm task")
421
 
422
 
423
  def _pw_extract_embed(shortcode):
 
887
  @app.get("/")
888
  async def root():
889
  return FileResponse("static/index.html")
890
+
891
+
892
+ @app.get("/health")
893
+ async def health_check():
894
+ """Lightweight health check for Railway. No external dependencies."""
895
+ return {"status": "ok"}
railway.toml CHANGED
@@ -1,3 +1,9 @@
1
  [build]
2
  builder = "DOCKERFILE"
3
  dockerfilePath = "Dockerfile"
 
 
 
 
 
 
 
1
  [build]
2
  builder = "DOCKERFILE"
3
  dockerfilePath = "Dockerfile"
4
+
5
+ [deploy]
6
+ healthcheckPath = "/health"
7
+ healthcheckTimeout = 120
8
+ restartPolicyType = "ON_FAILURE"
9
+ restartPolicyMaxRetries = 3
requirements.txt CHANGED
@@ -1,6 +1,6 @@
1
- fastapi
2
- uvicorn[standard]
3
- youtube-transcript-api
4
- playwright
5
- groq
6
- requests
 
1
+ fastapi==0.135.1
2
+ uvicorn[standard]==0.41.0
3
+ youtube-transcript-api==1.2.4
4
+ playwright==1.58.0
5
+ groq==1.1.1
6
+ requests==2.32.5
worker/index.js CHANGED
@@ -25,8 +25,8 @@ export default {
25
  headers.delete('cf-connecting-ip');
26
  headers.delete('cf-ray');
27
  headers.delete('cf-ipcountry');
28
- // Force uncompressed response so downstream clients can parse it
29
- // (Worker strips Content-Encoding header, so compressed bytes become unparsable)
30
  headers.set('Accept-Encoding', 'identity');
31
  // Override User-Agent with a browser UA
32
  headers.set('User-Agent', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36');
@@ -54,15 +54,15 @@ export default {
54
  const response = await fetch(targetUrl, fetchOptions);
55
  const body = await response.arrayBuffer();
56
 
57
- // Forward essential response headers including Content-Encoding
 
 
 
 
58
  const responseHeaders = {
59
  'Content-Type': response.headers.get('Content-Type') || 'text/html',
60
  'Access-Control-Allow-Origin': '*',
61
  };
62
- const contentEncoding = response.headers.get('Content-Encoding');
63
- if (contentEncoding) {
64
- responseHeaders['Content-Encoding'] = contentEncoding;
65
- }
66
 
67
  return new Response(body, {
68
  status: response.status,
 
25
  headers.delete('cf-connecting-ip');
26
  headers.delete('cf-ray');
27
  headers.delete('cf-ipcountry');
28
+ // Force uncompressed response prevents encoding mismatch
29
+ // (CF Workers auto-decompress bodies, so forwarding Content-Encoding would lie about the actual encoding)
30
  headers.set('Accept-Encoding', 'identity');
31
  // Override User-Agent with a browser UA
32
  headers.set('User-Agent', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36');
 
54
  const response = await fetch(targetUrl, fetchOptions);
55
  const body = await response.arrayBuffer();
56
 
57
+ // Build clean response headers
58
+ // CRITICAL: Do NOT forward Content-Encoding — CF Workers auto-decompress
59
+ // the body, so the raw bytes are always uncompressed regardless of what
60
+ // YouTube's Content-Encoding header says. Forwarding it would cause
61
+ // the downstream client to try decompressing already-decompressed data.
62
  const responseHeaders = {
63
  'Content-Type': response.headers.get('Content-Type') || 'text/html',
64
  'Access-Control-Allow-Origin': '*',
65
  };
 
 
 
 
66
 
67
  return new Response(body, {
68
  status: response.status,