Prathamesh Sarjerao Vaidya commited on
Commit
4f3a209
·
1 Parent(s): 484e5b0

made changes

Browse files
Dockerfile CHANGED
@@ -1,16 +1,13 @@
1
- # Optimized Dockerfile for Dance Movement Analyzer
2
- # Fixes MediaPipe models and FFmpeg H.264 encoder issues
3
-
4
  FROM python:3.10-slim
5
 
6
- # Set environment variables
7
  ENV PYTHONUNBUFFERED=1 \
8
  PYTHONDONTWRITEBYTECODE=1 \
9
  PIP_NO_CACHE_DIR=1 \
10
  PIP_DISABLE_PIP_VERSION_CHECK=1 \
11
  DEBIAN_FRONTEND=noninteractive
12
 
13
- # Install system dependencies including FFmpeg with H.264 support
14
  RUN apt-get update && apt-get install -y --no-install-recommends \
15
  libgl1 \
16
  libglib2.0-0 \
@@ -20,13 +17,14 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
20
  libgomp1 \
21
  ffmpeg \
22
  libavcodec-extra \
 
23
  curl \
 
24
  && rm -rf /var/lib/apt/lists/*
25
 
26
  # Verify FFmpeg has H.264 encoder
27
- RUN ffmpeg -codecs | grep h264 || echo "Warning: H.264 encoder may not be available"
28
 
29
- # Create app directory
30
  WORKDIR /app
31
 
32
  # Copy and install Python dependencies
@@ -34,58 +32,38 @@ COPY backend/requirements.txt .
34
  RUN pip install --no-cache-dir --upgrade pip && \
35
  pip install --no-cache-dir -r requirements.txt
36
 
37
- # Pre-download MediaPipe models (as root, before creating non-root user)
38
  RUN echo "Downloading MediaPipe Pose models..." && \
39
  python3 -c "import mediapipe as mp; \
40
- print('Downloading Lite model...'); \
41
- pose0 = mp.solutions.pose.Pose(model_complexity=0, min_detection_confidence=0.5); \
42
- pose0.close(); \
43
- print('✅ Lite model downloaded'); \
44
- print('Downloading Full model...'); \
45
- pose1 = mp.solutions.pose.Pose(model_complexity=1, min_detection_confidence=0.5); \
46
- pose1.close(); \
47
- print('✅ Full model downloaded'); \
48
- print('Downloading Heavy model...'); \
49
- pose2 = mp.solutions.pose.Pose(model_complexity=2, min_detection_confidence=0.5); \
50
- pose2.close(); \
51
- print('✅ Heavy model downloaded'); \
52
- print('✅ All models pre-downloaded successfully');" && \
53
- echo "MediaPipe models downloaded successfully"
54
 
55
- # Set proper permissions for MediaPipe models directory
56
  RUN chmod -R 755 /usr/local/lib/python3.10/site-packages/mediapipe
57
 
58
- # Create necessary directories
59
  RUN mkdir -p /app/uploads /app/outputs /app/logs
60
 
61
- # Copy application files
62
  COPY backend/app /app/app
63
  COPY frontend /app/frontend
64
-
65
- # Copy startup script
66
  COPY startup.sh /app/startup.sh
67
  RUN chmod +x /app/startup.sh
68
 
69
  # Create non-root user
70
- RUN useradd -m -u 1000 appuser
71
-
72
- # Set ownership of app directory
73
- RUN chown -R appuser:appuser /app
74
-
75
- # IMPORTANT: Keep MediaPipe models readable by non-root user
76
- RUN chmod -R 755 /usr/local/lib/python3.10/site-packages/mediapipe && \
77
  chown -R root:root /usr/local/lib/python3.10/site-packages/mediapipe
78
 
79
- # Switch to non-root user
80
  USER appuser
81
 
82
- # Expose ports
83
- EXPOSE 7860
84
- EXPOSE 8000
85
 
86
- # Health check
87
  HEALTHCHECK --interval=30s --timeout=10s --start-period=5m --retries=3 \
88
  CMD python -c "import requests; requests.get('http://localhost:7860/health')" || exit 1
89
 
90
- # Start application
91
  CMD ["/app/startup.sh"]
 
1
+ # Optimized Dockerfile with full H.264 support
 
 
2
  FROM python:3.10-slim
3
 
 
4
  ENV PYTHONUNBUFFERED=1 \
5
  PYTHONDONTWRITEBYTECODE=1 \
6
  PIP_NO_CACHE_DIR=1 \
7
  PIP_DISABLE_PIP_VERSION_CHECK=1 \
8
  DEBIAN_FRONTEND=noninteractive
9
 
10
+ # Install system dependencies with full ffmpeg and x264
11
  RUN apt-get update && apt-get install -y --no-install-recommends \
12
  libgl1 \
13
  libglib2.0-0 \
 
17
  libgomp1 \
18
  ffmpeg \
19
  libavcodec-extra \
20
+ libx264-dev \
21
  curl \
22
+ wget \
23
  && rm -rf /var/lib/apt/lists/*
24
 
25
  # Verify FFmpeg has H.264 encoder
26
+ RUN ffmpeg -codecs 2>/dev/null | grep -i h264 || echo "H.264 codec not available, will use fallback"
27
 
 
28
  WORKDIR /app
29
 
30
  # Copy and install Python dependencies
 
32
  RUN pip install --no-cache-dir --upgrade pip && \
33
  pip install --no-cache-dir -r requirements.txt
34
 
35
+ # Pre-download MediaPipe models
36
  RUN echo "Downloading MediaPipe Pose models..." && \
37
  python3 -c "import mediapipe as mp; \
38
+ pose0 = mp.solutions.pose.Pose(model_complexity=0, min_detection_confidence=0.5); pose0.close(); \
39
+ pose1 = mp.solutions.pose.Pose(model_complexity=1, min_detection_confidence=0.5); pose1.close(); \
40
+ pose2 = mp.solutions.pose.Pose(model_complexity=2, min_detection_confidence=0.5); pose2.close(); \
41
+ print('✅ All models pre-downloaded');" && \
42
+ echo "MediaPipe models downloaded"
 
 
 
 
 
 
 
 
 
43
 
44
+ # Set permissions
45
  RUN chmod -R 755 /usr/local/lib/python3.10/site-packages/mediapipe
46
 
47
+ # Create directories
48
  RUN mkdir -p /app/uploads /app/outputs /app/logs
49
 
50
+ # Copy application
51
  COPY backend/app /app/app
52
  COPY frontend /app/frontend
 
 
53
  COPY startup.sh /app/startup.sh
54
  RUN chmod +x /app/startup.sh
55
 
56
  # Create non-root user
57
+ RUN useradd -m -u 1000 appuser && \
58
+ chown -R appuser:appuser /app && \
59
+ chmod -R 755 /usr/local/lib/python3.10/site-packages/mediapipe && \
 
 
 
 
60
  chown -R root:root /usr/local/lib/python3.10/site-packages/mediapipe
61
 
 
62
  USER appuser
63
 
64
+ EXPOSE 7860 8000
 
 
65
 
 
66
  HEALTHCHECK --interval=30s --timeout=10s --start-period=5m --retries=3 \
67
  CMD python -c "import requests; requests.get('http://localhost:7860/health')" || exit 1
68
 
 
69
  CMD ["/app/startup.sh"]
backend/app/main.py CHANGED
@@ -503,25 +503,30 @@ async def download_video(session_id: str):
503
  if not output_path.exists():
504
  raise HTTPException(status_code=404, detail="Output file not found")
505
 
506
- # Use StreamingResponse to support range requests (needed by HTML5 video)
 
 
 
507
  def iterfile():
508
  with open(output_path, mode="rb") as file_like:
509
- yield from file_like
 
 
 
 
 
510
 
511
  return StreamingResponse(
512
  iterfile(),
513
- media_type="video/mp4", # ✅ Ensure correct MIME type
514
  headers={
515
- "Accept-Ranges": "bytes", # ✅ Allow browser seeking
516
- "Content-Disposition": f'inline; filename="analyzed_{session["filename"]}"'
 
 
 
517
  }
518
  )
519
-
520
- # return FileResponse(
521
- # path=output_path,
522
- # media_type="video/mp4",
523
- # filename=f"analyzed_{session['filename']}"
524
- # )
525
 
526
 
527
  @app.websocket("/ws/{session_id}")
 
503
  if not output_path.exists():
504
  raise HTTPException(status_code=404, detail="Output file not found")
505
 
506
+ # Get file size for Content-Length header
507
+ file_size = output_path.stat().st_size
508
+
509
+ # Use StreamingResponse with proper headers for browser video playback
510
  def iterfile():
511
  with open(output_path, mode="rb") as file_like:
512
+ chunk_size = 8192 # 8KB chunks
513
+ while True:
514
+ chunk = file_like.read(chunk_size)
515
+ if not chunk:
516
+ break
517
+ yield chunk
518
 
519
  return StreamingResponse(
520
  iterfile(),
521
+ media_type="video/mp4",
522
  headers={
523
+ "Accept-Ranges": "bytes",
524
+ "Content-Length": str(file_size),
525
+ "Content-Disposition": f'inline; filename="analyzed_{session["filename"]}"',
526
+ "Cache-Control": "no-cache",
527
+ "X-Content-Type-Options": "nosniff"
528
  }
529
  )
 
 
 
 
 
 
530
 
531
 
532
  @app.websocket("/ws/{session_id}")
backend/app/video_processor.py CHANGED
@@ -112,25 +112,12 @@ class VideoProcessor:
112
  # Open video for reading
113
  cap = cv2.VideoCapture(str(video_path))
114
 
115
- # Setup video writer
116
- # # fourcc = cv2.VideoWriter_fourcc(*'mp4v')
117
- # fourcc = cv2.VideoWriter_fourcc(*Config.OUTPUT_VIDEO_CODEC)
118
- # out = cv2.VideoWriter(
119
- # str(output_path),
120
- # fourcc,
121
- # video_info['fps'],
122
- # (video_info['width'], video_info['height'])
123
- # )
124
-
125
- # if not out.isOpened():
126
- # raise ValueError(f"Cannot create output video: {output_path}")
127
-
128
  # Setup video writer with codec fallback
129
  codecs_to_try = [
130
- ('mp4v', 'MPEG-4'), # Best compatibility
131
- ('avc1', 'H.264'), # High quality (if available)
132
- ('XVID', 'Xvid'), # Fallback option
133
- ('MJPG', 'Motion JPEG') # Last resort
134
  ]
135
 
136
  out = None
 
112
  # Open video for reading
113
  cap = cv2.VideoCapture(str(video_path))
114
 
 
 
 
 
 
 
 
 
 
 
 
 
 
115
  # Setup video writer with codec fallback
116
  codecs_to_try = [
117
+ ('avc1', 'H.264'), # Try H.264 first
118
+ ('mp4v', 'MPEG-4'), # Fallback to MPEG-4
119
+ ('XVID', 'Xvid'),
120
+ ('MJPG', 'Motion JPEG')
121
  ]
122
 
123
  out = None
frontend/index.html CHANGED
@@ -100,16 +100,26 @@
100
  </div>
101
  <div class="video-container">
102
  <h3>Analyzed Video</h3>
103
- <!-- <video id="analyzedVideo" controls></video> -->
104
- <video id="analyzedVideo" controls preload="metadata" playsinline>
 
 
 
 
105
  Your browser does not support the video tag.
106
  </video>
107
- <div class="video-fallback" id="videoFallback" style="display: none; background: #ffebee; padding: 10px; border-radius: 5px; margin-top: 10px;">
108
- <p>Video cannot be played in browser. <a id="downloadFallback" href="#" style="color: #ef4444; font-weight: bold;">Download instead</a></p>
 
 
 
 
 
 
 
 
 
109
  </div>
110
- <!-- <button class="btn btn-download" id="downloadBtn">
111
- 💾 Download
112
- </button> -->
113
  </div>
114
  </div>
115
  </div>
 
100
  </div>
101
  <div class="video-container">
102
  <h3>Analyzed Video</h3>
103
+ <video
104
+ id="analyzedVideo"
105
+ controls
106
+ preload="metadata"
107
+ playsinline
108
+ style="max-width: 100%; height: auto; background: #000;">
109
  Your browser does not support the video tag.
110
  </video>
111
+ <div class="video-fallback" id="videoFallback" style="display: none; background: #ffebee; padding: 15px; border-radius: 8px; margin-top: 10px; text-align: center;">
112
+ <p style="margin: 0 0 10px 0; color: #c62828; font-weight: 500;">
113
+ ⚠️ Video cannot be played in browser
114
+ </p>
115
+ <a
116
+ id="downloadFallback"
117
+ href="#"
118
+ download
119
+ style="display: inline-block; padding: 10px 20px; background: #ef4444; color: white; text-decoration: none; border-radius: 6px; font-weight: bold; transition: background 0.3s;">
120
+ 💾 Download Video Instead
121
+ </a>
122
  </div>
 
 
 
123
  </div>
124
  </div>
125
  </div>
frontend/js/app.js CHANGED
@@ -322,7 +322,6 @@ async function handleAnalysisComplete(message) {
322
 
323
  } catch (error) {
324
  console.error('Error fetching results:', error);
325
- // Fallback to message results if API call fails
326
  AppState.results = message.results;
327
  }
328
 
@@ -330,11 +329,34 @@ async function handleAnalysisComplete(message) {
330
  elements.processingSection.style.display = 'none';
331
  elements.resultsSection.style.display = 'block';
332
 
333
- // Load analyzed video
334
  const videoUrl = `${API_BASE_URL}/api/download/${AppState.sessionId}`;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
335
  elements.analyzedVideo.src = videoUrl;
 
336
 
337
- // Display results with full data
338
  displayResults(AppState.results);
339
 
340
  // Setup video sync
 
322
 
323
  } catch (error) {
324
  console.error('Error fetching results:', error);
 
325
  AppState.results = message.results;
326
  }
327
 
 
329
  elements.processingSection.style.display = 'none';
330
  elements.resultsSection.style.display = 'block';
331
 
332
+ // Load analyzed video with proper error handling
333
  const videoUrl = `${API_BASE_URL}/api/download/${AppState.sessionId}`;
334
+
335
+ // Set up error handler BEFORE setting src
336
+ elements.analyzedVideo.onerror = (e) => {
337
+ console.error("Analyzed video failed to load:", e);
338
+ console.log("Video URL:", videoUrl);
339
+ elements.videoFallback.style.display = 'block';
340
+ elements.analyzedVideo.style.display = 'none';
341
+ document.getElementById('downloadFallback').href = videoUrl;
342
+ document.getElementById('downloadFallback').download = `analyzed_${AppState.uploadedFile?.name || 'video.mp4'}`;
343
+ };
344
+
345
+ // Set up success handler
346
+ elements.analyzedVideo.onloadedmetadata = () => {
347
+ console.log("✅ Analyzed video loaded successfully");
348
+ console.log("Video duration:", elements.analyzedVideo.duration);
349
+ console.log("Video dimensions:", elements.analyzedVideo.videoWidth, 'x', elements.analyzedVideo.videoHeight);
350
+ elements.videoFallback.style.display = 'none';
351
+ elements.analyzedVideo.style.display = 'block';
352
+ };
353
+
354
+ // Set video source
355
+ console.log("Loading analyzed video from:", videoUrl);
356
  elements.analyzedVideo.src = videoUrl;
357
+ elements.analyzedVideo.load(); // Force reload
358
 
359
+ // Display results
360
  displayResults(AppState.results);
361
 
362
  // Setup video sync