Spaces:
Paused
Paused
storage
Browse files- app/main.py +7 -2
- app/streamlit_app.py +31 -1
- app/utils/video_downloader.py +82 -0
app/main.py
CHANGED
|
@@ -13,7 +13,7 @@ load_dotenv()
|
|
| 13 |
# Add the app directory to the path
|
| 14 |
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
| 15 |
|
| 16 |
-
from app.utils.video_downloader import download_youtube_video
|
| 17 |
from app.utils.video_processor import process_video
|
| 18 |
from app.models.pose_estimator import analyze_pose
|
| 19 |
from app.models.swing_analyzer import segment_swing, analyze_trajectory
|
|
@@ -38,6 +38,7 @@ def main():
|
|
| 38 |
if sample_rate_input.isdigit():
|
| 39 |
sample_rate = max(1, min(10, int(sample_rate_input)))
|
| 40 |
|
|
|
|
| 41 |
try:
|
| 42 |
# Step 3: Download the video
|
| 43 |
print("\nDownloading video...")
|
|
@@ -95,7 +96,11 @@ def main():
|
|
| 95 |
|
| 96 |
except Exception as e:
|
| 97 |
print(f"\nError: {str(e)}")
|
| 98 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 99 |
|
| 100 |
|
| 101 |
if __name__ == "__main__":
|
|
|
|
| 13 |
# Add the app directory to the path
|
| 14 |
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
| 15 |
|
| 16 |
+
from app.utils.video_downloader import download_youtube_video, cleanup_video_file
|
| 17 |
from app.utils.video_processor import process_video
|
| 18 |
from app.models.pose_estimator import analyze_pose
|
| 19 |
from app.models.swing_analyzer import segment_swing, analyze_trajectory
|
|
|
|
| 38 |
if sample_rate_input.isdigit():
|
| 39 |
sample_rate = max(1, min(10, int(sample_rate_input)))
|
| 40 |
|
| 41 |
+
video_path = None # Initialize video_path for cleanup
|
| 42 |
try:
|
| 43 |
# Step 3: Download the video
|
| 44 |
print("\nDownloading video...")
|
|
|
|
| 96 |
|
| 97 |
except Exception as e:
|
| 98 |
print(f"\nError: {str(e)}")
|
| 99 |
+
finally:
|
| 100 |
+
# Clean up the original downloaded video file after processing
|
| 101 |
+
if video_path:
|
| 102 |
+
print("\nCleaning up downloaded video file...")
|
| 103 |
+
cleanup_video_file(video_path)
|
| 104 |
|
| 105 |
|
| 106 |
if __name__ == "__main__":
|
app/streamlit_app.py
CHANGED
|
@@ -19,7 +19,7 @@ load_dotenv()
|
|
| 19 |
# Add the app directory to the path
|
| 20 |
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
| 21 |
|
| 22 |
-
from app.utils.video_downloader import download_youtube_video, download_pro_reference
|
| 23 |
from app.utils.video_processor import process_video
|
| 24 |
from app.models.pose_estimator import analyze_pose
|
| 25 |
from app.models.swing_analyzer import segment_swing, analyze_trajectory
|
|
@@ -92,6 +92,7 @@ def main():
|
|
| 92 |
"""Main Streamlit application"""
|
| 93 |
st.title("Par-ity Project: Golf Swing Analysis 🏌️♀️")
|
| 94 |
st.write("Founded to address the gender gap in golf participation and access to quality coaching resources, Par-ity Project is a technology-driven initiative empowering girls in golf through innovative AI based swing analysis. This technology uses computer vision and machine learning algorithms to analyze golf swings and provide personalized feedback to improve technique and performance.")
|
|
|
|
| 95 |
# Initialize session state for storing analysis results
|
| 96 |
if 'video_analyzed' not in st.session_state:
|
| 97 |
st.session_state.video_analyzed = False
|
|
@@ -107,10 +108,32 @@ def main():
|
|
| 107 |
}
|
| 108 |
if 'pro_reference_path' not in st.session_state:
|
| 109 |
st.session_state.pro_reference_path = None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 110 |
|
| 111 |
# Sidebar for configuration
|
| 112 |
st.sidebar.title("Configuration")
|
| 113 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 114 |
# Check available LLM services
|
| 115 |
llm_services = check_llm_services()
|
| 116 |
any_service_available = llm_services['ollama'][
|
|
@@ -289,6 +312,10 @@ def main():
|
|
| 289 |
'prompt': prompt
|
| 290 |
}
|
| 291 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 292 |
# Present the options after analysis
|
| 293 |
st.subheader("What would you like to do next?")
|
| 294 |
options_col1, options_col2, options_col3 = st.columns(3)
|
|
@@ -311,6 +338,9 @@ def main():
|
|
| 311 |
except Exception as e:
|
| 312 |
st.error(f"Error during analysis: {str(e)}")
|
| 313 |
st.session_state.video_analyzed = False
|
|
|
|
|
|
|
|
|
|
| 314 |
|
| 315 |
# Show action buttons and their results (only if analysis is complete)
|
| 316 |
if st.session_state.video_analyzed:
|
|
|
|
| 19 |
# Add the app directory to the path
|
| 20 |
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
| 21 |
|
| 22 |
+
from app.utils.video_downloader import download_youtube_video, download_pro_reference, cleanup_video_file, cleanup_downloads_directory
|
| 23 |
from app.utils.video_processor import process_video
|
| 24 |
from app.models.pose_estimator import analyze_pose
|
| 25 |
from app.models.swing_analyzer import segment_swing, analyze_trajectory
|
|
|
|
| 92 |
"""Main Streamlit application"""
|
| 93 |
st.title("Par-ity Project: Golf Swing Analysis 🏌️♀️")
|
| 94 |
st.write("Founded to address the gender gap in golf participation and access to quality coaching resources, Par-ity Project is a technology-driven initiative empowering girls in golf through innovative AI based swing analysis. This technology uses computer vision and machine learning algorithms to analyze golf swings and provide personalized feedback to improve technique and performance.")
|
| 95 |
+
|
| 96 |
# Initialize session state for storing analysis results
|
| 97 |
if 'video_analyzed' not in st.session_state:
|
| 98 |
st.session_state.video_analyzed = False
|
|
|
|
| 108 |
}
|
| 109 |
if 'pro_reference_path' not in st.session_state:
|
| 110 |
st.session_state.pro_reference_path = None
|
| 111 |
+
|
| 112 |
+
# Add session cleanup - clean up old files when starting a new session
|
| 113 |
+
if 'session_initialized' not in st.session_state:
|
| 114 |
+
cleanup_result = cleanup_downloads_directory(keep_annotated=True)
|
| 115 |
+
if cleanup_result.get('files_removed', 0) > 0:
|
| 116 |
+
st.info(f"🗑️ Cleaned up {cleanup_result['files_removed']} old files ({cleanup_result['space_freed_mb']} MB freed)")
|
| 117 |
+
st.session_state.session_initialized = True
|
| 118 |
|
| 119 |
# Sidebar for configuration
|
| 120 |
st.sidebar.title("Configuration")
|
| 121 |
|
| 122 |
+
# Add Reset Session button
|
| 123 |
+
st.sidebar.markdown("---")
|
| 124 |
+
if st.sidebar.button("🗑️ Reset Session & Clean Files", help="Clear all session data and remove downloaded files"):
|
| 125 |
+
# Clean up downloads directory
|
| 126 |
+
cleanup_result = cleanup_downloads_directory(keep_annotated=False) # Remove all files including annotated
|
| 127 |
+
|
| 128 |
+
# Clear session state
|
| 129 |
+
for key in list(st.session_state.keys()):
|
| 130 |
+
del st.session_state[key]
|
| 131 |
+
|
| 132 |
+
st.sidebar.success(f"Session reset! Cleaned {cleanup_result.get('files_removed', 0)} files ({cleanup_result.get('space_freed_mb', 0)} MB freed)")
|
| 133 |
+
st.rerun()
|
| 134 |
+
|
| 135 |
+
st.sidebar.markdown("---")
|
| 136 |
+
|
| 137 |
# Check available LLM services
|
| 138 |
llm_services = check_llm_services()
|
| 139 |
any_service_available = llm_services['ollama'][
|
|
|
|
| 312 |
'prompt': prompt
|
| 313 |
}
|
| 314 |
|
| 315 |
+
# Clean up the original video file after processing (keep frames in memory)
|
| 316 |
+
st.info("🗑️ Cleaning up original video file to save space...")
|
| 317 |
+
cleanup_video_file(video_path)
|
| 318 |
+
|
| 319 |
# Present the options after analysis
|
| 320 |
st.subheader("What would you like to do next?")
|
| 321 |
options_col1, options_col2, options_col3 = st.columns(3)
|
|
|
|
| 338 |
except Exception as e:
|
| 339 |
st.error(f"Error during analysis: {str(e)}")
|
| 340 |
st.session_state.video_analyzed = False
|
| 341 |
+
# Clean up on error as well
|
| 342 |
+
if video_path and os.path.exists(video_path):
|
| 343 |
+
cleanup_video_file(video_path)
|
| 344 |
|
| 345 |
# Show action buttons and their results (only if analysis is complete)
|
| 346 |
if st.session_state.video_analyzed:
|
app/utils/video_downloader.py
CHANGED
|
@@ -6,6 +6,88 @@ import os
|
|
| 6 |
import yt_dlp
|
| 7 |
|
| 8 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9 |
def download_youtube_video(url, output_dir="downloads"):
|
| 10 |
"""
|
| 11 |
Download a YouTube video from the provided URL using yt-dlp
|
|
|
|
| 6 |
import yt_dlp
|
| 7 |
|
| 8 |
|
| 9 |
+
def cleanup_video_file(video_path):
|
| 10 |
+
"""
|
| 11 |
+
Delete a specific video file after processing
|
| 12 |
+
|
| 13 |
+
Args:
|
| 14 |
+
video_path (str): Path to the video file to delete
|
| 15 |
+
|
| 16 |
+
Returns:
|
| 17 |
+
bool: True if file was deleted successfully, False otherwise
|
| 18 |
+
"""
|
| 19 |
+
try:
|
| 20 |
+
if os.path.exists(video_path):
|
| 21 |
+
os.remove(video_path)
|
| 22 |
+
print(f"Cleaned up video file: {video_path}")
|
| 23 |
+
return True
|
| 24 |
+
else:
|
| 25 |
+
print(f"Video file not found for cleanup: {video_path}")
|
| 26 |
+
return False
|
| 27 |
+
except Exception as e:
|
| 28 |
+
print(f"Error cleaning up video file {video_path}: {str(e)}")
|
| 29 |
+
return False
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
def cleanup_downloads_directory(output_dir="downloads", keep_annotated=True):
|
| 33 |
+
"""
|
| 34 |
+
Clean up downloaded videos from the downloads directory
|
| 35 |
+
|
| 36 |
+
Args:
|
| 37 |
+
output_dir (str): Directory containing downloaded videos
|
| 38 |
+
keep_annotated (bool): Whether to keep annotated videos (default: True)
|
| 39 |
+
|
| 40 |
+
Returns:
|
| 41 |
+
dict: Cleanup results with files removed and space freed
|
| 42 |
+
"""
|
| 43 |
+
try:
|
| 44 |
+
if not os.path.exists(output_dir):
|
| 45 |
+
return {"files_removed": 0, "space_freed_mb": 0}
|
| 46 |
+
|
| 47 |
+
files_removed = 0
|
| 48 |
+
space_freed = 0
|
| 49 |
+
|
| 50 |
+
for filename in os.listdir(output_dir):
|
| 51 |
+
file_path = os.path.join(output_dir, filename)
|
| 52 |
+
|
| 53 |
+
# Skip if not a file
|
| 54 |
+
if not os.path.isfile(file_path):
|
| 55 |
+
continue
|
| 56 |
+
|
| 57 |
+
# Skip annotated videos if keep_annotated is True
|
| 58 |
+
if keep_annotated and "_annotated" in filename:
|
| 59 |
+
continue
|
| 60 |
+
|
| 61 |
+
# Skip pro reference videos (they can be reused)
|
| 62 |
+
if "pro_reference" in filename:
|
| 63 |
+
continue
|
| 64 |
+
|
| 65 |
+
# Get file size before deletion
|
| 66 |
+
try:
|
| 67 |
+
file_size = os.path.getsize(file_path)
|
| 68 |
+
space_freed += file_size
|
| 69 |
+
|
| 70 |
+
# Remove the file
|
| 71 |
+
os.remove(file_path)
|
| 72 |
+
files_removed += 1
|
| 73 |
+
print(f"Cleaned up: {filename}")
|
| 74 |
+
|
| 75 |
+
except Exception as e:
|
| 76 |
+
print(f"Error removing {filename}: {str(e)}")
|
| 77 |
+
|
| 78 |
+
# Convert bytes to MB
|
| 79 |
+
space_freed_mb = space_freed / (1024 * 1024)
|
| 80 |
+
|
| 81 |
+
return {
|
| 82 |
+
"files_removed": files_removed,
|
| 83 |
+
"space_freed_mb": round(space_freed_mb, 2)
|
| 84 |
+
}
|
| 85 |
+
|
| 86 |
+
except Exception as e:
|
| 87 |
+
print(f"Error during cleanup: {str(e)}")
|
| 88 |
+
return {"error": str(e)}
|
| 89 |
+
|
| 90 |
+
|
| 91 |
def download_youtube_video(url, output_dir="downloads"):
|
| 92 |
"""
|
| 93 |
Download a YouTube video from the provided URL using yt-dlp
|