Prathamesh Sarjerao Vaidya commited on
Commit
b5cf10c
Β·
1 Parent(s): 2dc4e60

made changes

Browse files
Files changed (3) hide show
  1. Dockerfile +40 -52
  2. backend/app/main.py +15 -16
  3. startup.sh +2 -31
Dockerfile CHANGED
@@ -1,8 +1,7 @@
1
- # Multi-stage Dockerfile for Dance Movement Analyzer
2
- # Optimized for Hugging Face Spaces with MediaPipe model pre-download
3
 
4
- # Stage 1: Base image with dependencies
5
- FROM python:3.10-slim as base
6
 
7
  # Set environment variables
8
  ENV PYTHONUNBUFFERED=1 \
@@ -13,7 +12,7 @@ ENV PYTHONUNBUFFERED=1 \
13
 
14
  # Install system dependencies
15
  RUN apt-get update && apt-get install -y --no-install-recommends \
16
- libgl1 \
17
  libglib2.0-0 \
18
  libsm6 \
19
  libxext6 \
@@ -26,74 +25,63 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
26
  # Create app directory
27
  WORKDIR /app
28
 
29
- # Stage 2: Dependencies installation
30
- FROM base as dependencies
31
-
32
- # Copy requirements first for better caching
33
  COPY backend/requirements.txt .
34
-
35
- # Install Python dependencies
36
- RUN pip install --no-cache-dir -r requirements.txt
37
-
38
- # Pre-download ALL MediaPipe models (as root before user switch)
39
- # This downloads the models to the pip packages directory
40
- RUN python3 -c "\
41
- import mediapipe as mp; \
42
- print('Downloading MediaPipe Pose models...'); \
43
- # Model complexity 0 (Lite) \
44
- pose0 = mp.solutions.pose.Pose(model_complexity=0, min_detection_confidence=0.5); \
45
- pose0.close(); \
46
- print('βœ… Lite model downloaded'); \
47
- # Model complexity 1 (Full) \
48
- pose1 = mp.solutions.pose.Pose(model_complexity=1, min_detection_confidence=0.5); \
49
- pose1.close(); \
50
- print('βœ… Full model downloaded'); \
51
- # Model complexity 2 (Heavy) \
52
- pose2 = mp.solutions.pose.Pose(model_complexity=2, min_detection_confidence=0.5); \
53
- pose2.close(); \
54
- print('βœ… Heavy model downloaded'); \
55
- print('βœ… All MediaPipe models pre-downloaded successfully'); \
56
- "
57
-
58
- # Stage 3: Production image
59
- FROM base as production
60
-
61
- # Copy installed packages from dependencies stage (includes downloaded models)
62
- COPY --from=dependencies /usr/local/lib/python3.10/site-packages /usr/local/lib/python3.10/site-packages
63
- COPY --from=dependencies /usr/local/bin /usr/local/bin
64
 
65
  # Create necessary directories
66
  RUN mkdir -p /app/uploads /app/outputs /app/logs
67
 
68
- # Copy backend application
69
  COPY backend/app /app/app
70
-
71
- # Copy frontend files
72
  COPY frontend /app/frontend
73
 
74
  # Copy startup script
75
  COPY startup.sh /app/startup.sh
76
-
77
- # Make startup script executable
78
  RUN chmod +x /app/startup.sh
79
 
80
- # Set permissions for all app files
81
- RUN chmod -R 755 /app
 
 
 
82
 
83
- # Create non-root user for security
84
- RUN useradd -m -u 1000 appuser && \
85
- chown -R appuser:appuser /app
86
 
87
  # Switch to non-root user
88
  USER appuser
89
 
90
- # Expose ports (7860 for Hugging Face, 8000 for local)
91
  EXPOSE 8000
92
  EXPOSE 7860
93
 
94
- # Health check with 5 minute startup period
95
  HEALTHCHECK --interval=30s --timeout=10s --start-period=5m --retries=3 \
96
  CMD python -c "import requests; requests.get('http://localhost:8000/health')" || exit 1
97
 
98
- # Run the application using startup script
99
  CMD ["/app/startup.sh"]
 
1
+ # Optimized Dockerfile for Dance Movement Analyzer
2
+ # Fixes MediaPipe model permission issues
3
 
4
+ FROM python:3.10-slim
 
5
 
6
  # Set environment variables
7
  ENV PYTHONUNBUFFERED=1 \
 
12
 
13
  # Install system dependencies
14
  RUN apt-get update && apt-get install -y --no-install-recommends \
15
+ libgl1-mesa-glx \
16
  libglib2.0-0 \
17
  libsm6 \
18
  libxext6 \
 
25
  # Create app directory
26
  WORKDIR /app
27
 
28
+ # Copy and install Python dependencies
 
 
 
29
  COPY backend/requirements.txt .
30
+ RUN pip install --no-cache-dir --upgrade pip && \
31
+ pip install --no-cache-dir -r requirements.txt
32
+
33
+ # Pre-download MediaPipe models (as root, before creating non-root user)
34
+ RUN echo "Downloading MediaPipe Pose models..." && \
35
+ python3 -c "import mediapipe as mp; \
36
+ print('Downloading Lite model...'); \
37
+ pose0 = mp.solutions.pose.Pose(model_complexity=0, min_detection_confidence=0.5); \
38
+ pose0.close(); \
39
+ print('βœ… Lite model downloaded'); \
40
+ print('Downloading Full model...'); \
41
+ pose1 = mp.solutions.pose.Pose(model_complexity=1, min_detection_confidence=0.5); \
42
+ pose1.close(); \
43
+ print('βœ… Full model downloaded'); \
44
+ print('Downloading Heavy model...'); \
45
+ pose2 = mp.solutions.pose.Pose(model_complexity=2, min_detection_confidence=0.5); \
46
+ pose2.close(); \
47
+ print('βœ… Heavy model downloaded'); \
48
+ print('βœ… All models pre-downloaded successfully');" && \
49
+ echo "MediaPipe models downloaded successfully"
50
+
51
+ # Set proper permissions for MediaPipe models directory
52
+ RUN chmod -R 755 /usr/local/lib/python3.10/site-packages/mediapipe
 
 
 
 
 
 
 
53
 
54
  # Create necessary directories
55
  RUN mkdir -p /app/uploads /app/outputs /app/logs
56
 
57
+ # Copy application files
58
  COPY backend/app /app/app
 
 
59
  COPY frontend /app/frontend
60
 
61
  # Copy startup script
62
  COPY startup.sh /app/startup.sh
 
 
63
  RUN chmod +x /app/startup.sh
64
 
65
+ # Create non-root user
66
+ RUN useradd -m -u 1000 appuser
67
+
68
+ # Set ownership of app directory
69
+ RUN chown -R appuser:appuser /app
70
 
71
+ # IMPORTANT: Keep MediaPipe models readable by non-root user
72
+ RUN chmod -R 755 /usr/local/lib/python3.10/site-packages/mediapipe && \
73
+ chown -R root:root /usr/local/lib/python3.10/site-packages/mediapipe
74
 
75
  # Switch to non-root user
76
  USER appuser
77
 
78
+ # Expose ports
79
  EXPOSE 8000
80
  EXPOSE 7860
81
 
82
+ # Health check
83
  HEALTHCHECK --interval=30s --timeout=10s --start-period=5m --retries=3 \
84
  CMD python -c "import requests; requests.get('http://localhost:8000/health')" || exit 1
85
 
86
+ # Start application
87
  CMD ["/app/startup.sh"]
backend/app/main.py CHANGED
@@ -34,8 +34,7 @@ global_processor: Optional[VideoProcessor] = None
34
 
35
  @asynccontextmanager
36
  async def lifespan(app: FastAPI):
37
- """Lifespan event handler - models already downloaded in Docker build"""
38
- global global_processor
39
 
40
  # Startup
41
  logger.info("πŸš€ Starting Dance Movement Analyzer...")
@@ -44,15 +43,9 @@ async def lifespan(app: FastAPI):
44
  Config.initialize_folders()
45
  logger.info("βœ… Folders initialized")
46
 
47
- # Initialize VideoProcessor (models already downloaded, just instantiate)
48
- logger.info("πŸ“¦ Initializing Video Processor...")
49
- try:
50
- global_processor = VideoProcessor()
51
- logger.info("βœ… Video Processor initialized successfully")
52
- except Exception as e:
53
- logger.error(f"❌ Error initializing Video Processor: {e}")
54
- # Continue anyway - will retry on first request
55
- global_processor = None
56
 
57
  logger.info("πŸŽ‰ Application startup complete!")
58
 
@@ -60,9 +53,6 @@ async def lifespan(app: FastAPI):
60
 
61
  # Shutdown
62
  logger.info("πŸ‘‹ Shutting down application...")
63
- if global_processor is not None:
64
- # Cleanup if needed
65
- pass
66
 
67
  # Initialize FastAPI app with lifespan
68
  app = FastAPI(
@@ -175,11 +165,20 @@ async def health_check():
175
  return {
176
  "status": "healthy",
177
  "models_loaded": global_processor is not None,
 
178
  "timestamp": datetime.now().isoformat(),
179
- "active_sessions": len(processing_sessions),
180
- "active_connections": len(manager.active_connections)
181
  }
182
 
 
 
 
 
 
 
 
 
 
183
  @app.post("/api/upload")
184
  async def upload_video(file: UploadFile = File(...)):
185
  """Upload a video file for processing"""
 
34
 
35
  @asynccontextmanager
36
  async def lifespan(app: FastAPI):
37
+ """Lifespan event handler - lazy model loading"""
 
38
 
39
  # Startup
40
  logger.info("πŸš€ Starting Dance Movement Analyzer...")
 
43
  Config.initialize_folders()
44
  logger.info("βœ… Folders initialized")
45
 
46
+ # Don't initialize VideoProcessor here - let it lazy load on first request
47
+ # This avoids the permission error during startup
48
+ logger.info("πŸ“¦ Models pre-downloaded, will initialize on first request")
 
 
 
 
 
 
49
 
50
  logger.info("πŸŽ‰ Application startup complete!")
51
 
 
53
 
54
  # Shutdown
55
  logger.info("πŸ‘‹ Shutting down application...")
 
 
 
56
 
57
  # Initialize FastAPI app with lifespan
58
  app = FastAPI(
 
165
  return {
166
  "status": "healthy",
167
  "models_loaded": global_processor is not None,
168
+ "models_ready": True, # Models are pre-downloaded
169
  "timestamp": datetime.now().isoformat(),
170
+ "active_sessions": len(processing_sessions)
 
171
  }
172
 
173
+ def get_video_processor() -> VideoProcessor:
174
+ """Get or create the global VideoProcessor instance"""
175
+ global global_processor
176
+ if global_processor is None:
177
+ logger.info("Initializing VideoProcessor (first use)...")
178
+ global_processor = VideoProcessor()
179
+ logger.info("βœ… VideoProcessor initialized")
180
+ return global_processor
181
+
182
  @app.post("/api/upload")
183
  async def upload_video(file: UploadFile = File(...)):
184
  """Upload a video file for processing"""
startup.sh CHANGED
@@ -2,42 +2,13 @@
2
  set -e
3
 
4
  echo "πŸš€ Starting Dance Movement Analyzer..."
5
-
6
- # Verify MediaPipe models are available
7
- echo "πŸ“¦ Verifying MediaPipe models..."
8
- python3 -c "
9
- import mediapipe as mp
10
- import logging
11
- import os
12
-
13
- logging.basicConfig(level=logging.INFO)
14
- logger = logging.getLogger(__name__)
15
-
16
- try:
17
- # Quick verification that models are accessible
18
- logger.info('Checking MediaPipe installation...')
19
-
20
- # Verify model files exist
21
- model_path = '/usr/local/lib/python3.10/site-packages/mediapipe/modules/pose_landmark'
22
- if os.path.exists(model_path):
23
- logger.info(f'βœ… Model directory found: {model_path}')
24
- model_files = os.listdir(model_path)
25
- logger.info(f'Available models: {len(model_files)} files')
26
- else:
27
- logger.warning('⚠️ Model directory not found, models will load on first use')
28
-
29
- logger.info('βœ… MediaPipe ready')
30
-
31
- except Exception as e:
32
- logger.error(f'❌ Error verifying models: {e}')
33
- # Continue anyway - application will handle it
34
- "
35
 
36
  # Detect port (Hugging Face uses 7860, local uses 8000)
37
  PORT=${PORT:-8000}
38
 
39
  echo "🎬 Starting Uvicorn server on port $PORT..."
40
- echo "πŸ“ Application will be available at http://0.0.0.0:$PORT"
41
  echo ""
42
 
43
  # Start the application
 
2
  set -e
3
 
4
  echo "πŸš€ Starting Dance Movement Analyzer..."
5
+ echo "πŸ“¦ MediaPipe models pre-downloaded during build"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6
 
7
  # Detect port (Hugging Face uses 7860, local uses 8000)
8
  PORT=${PORT:-8000}
9
 
10
  echo "🎬 Starting Uvicorn server on port $PORT..."
11
+ echo "πŸ“ Application available at http://0.0.0.0:$PORT"
12
  echo ""
13
 
14
  # Start the application