File size: 9,163 Bytes
f72c2f8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Bodybuilding Pose Analyzer</title>
    <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/tailwind.min.css" rel="stylesheet">
</head>
<body class="bg-gray-100 min-h-screen">
    <div class="container mx-auto px-4 py-8">
        <h1 class="text-4xl font-bold text-center mb-8">Bodybuilding Pose Analyzer</h1>
        
        <div class="max-w-2xl mx-auto bg-white rounded-lg shadow-lg p-6">
            <div class="mb-6">
                <h2 class="text-2xl font-semibold mb-4">Upload Video</h2>
                <form id="uploadForm" class="space-y-4">
                    <div class="border-2 border-dashed border-gray-300 rounded-lg p-6 text-center">
                        <input type="file" id="videoInput" accept="video/*" class="hidden">
                        <label for="videoInput" class="cursor-pointer">
                            <div class="text-gray-600">
                                <svg class="mx-auto h-12 w-12" stroke="currentColor" fill="none" viewBox="0 0 48 48">
                                    <path d="M28 8H12a4 4 0 00-4 4v20m32-12v8m0 0v8a4 4 0 01-4 4H12a4 4 0 01-4-4v-4m32-4l-3.172-3.172a4 4 0 00-5.656 0L28 28M8 32l9.172-9.172a4 4 0 015.656 0L28 28m0 0l4 4m4-24h8m-4-4v8m-12 4h.02" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
                                </svg>
                                <p class="mt-1">Click to upload a video</p>
                                <p id="fileName" class="text-sm text-gray-500 mt-1"></p>
                            </div>
                        </label>
                    </div>

                    <div>
                        <label class="block text-sm font-medium text-gray-700">Choose Model:</label>
                        <div class="mt-1 flex rounded-md shadow-sm">
                            <div class="relative flex items-stretch flex-grow focus-within:z-10">
                                <label class="inline-flex items-center">
                                    <input type="radio" class="form-radio" name="model_choice" value="movenet" checked>
                                    <span class="ml-2">Gladiator BB</span>
                                </label>
                                <label class="inline-flex items-center ml-6">
                                    <input type="radio" class="form-radio" name="model_choice" value="Gladiator SupaDot">
                                    <span class="ml-2">Gladiator SupaDot</span>
                                </label>
                            </div>
                        </div>
                    </div>
                    <div id="gladiatorBBOptions" class="space-y-4">
                        <div>
                            <label class="block text-sm font-medium text-gray-700">Gladiator BB Variant:</label>
                            <div class="mt-1 flex rounded-md shadow-sm">
                                <div class="relative flex items-stretch flex-grow focus-within:z-10">
                                    <label class="inline-flex items-center">
                                        <input type="radio" class="form-radio" name="movenet_variant" value="lightning" checked>
                                        <span class="ml-2">Lightning (Faster, Less Accurate)</span>
                                    </label>
                                    <label class="inline-flex items-center ml-6">
                                        <input type="radio" class="form-radio" name="movenet_variant" value="thunder">
                                        <span class="ml-2">Thunder (Slower, More Accurate)</span>
                                    </label>
                                </div>
                            </div>
                        </div>
                    </div>
                    
                    <button type="submit" class="w-full bg-blue-500 text-white py-2 px-4 rounded-lg hover:bg-blue-600 transition duration-200">
                        Process Video
                    </button>
                </form>
            </div>
            
            <div id="result" class="hidden">
                <h2 class="text-2xl font-semibold mb-4">Results</h2>
                <div class="aspect-w-16 aspect-h-9">
                    <video id="outputVideo" controls class="w-full rounded-lg"></video>
                </div>
            </div>
            
            <div id="loading" class="hidden">
                <div class="flex items-center justify-center">
                    <div class="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-500"></div>
                </div>
                <p class="text-center mt-4">Processing video...</p>
            </div>
        </div>
    </div>

    <script>
        document.getElementById('videoInput').addEventListener('change', function() {
            const fileName = this.files[0] ? this.files[0].name : 'No file selected';
            document.getElementById('fileName').textContent = fileName;
        });

        document.querySelectorAll('input[name="model_choice"]').forEach(radio => {
            radio.addEventListener('change', function() {
                const gladiatorBBOptions = document.getElementById('gladiatorBBOptions');
                if (this.value === 'movenet') {
                    gladiatorBBOptions.classList.remove('hidden');
                } else {
                    gladiatorBBOptions.classList.add('hidden');
                }
            });
        });

        // Trigger change event on page load for the initially checked model_choice
        document.querySelector('input[name="model_choice"]:checked').dispatchEvent(new Event('change'));

        document.getElementById('uploadForm').addEventListener('submit', async (e) => {
            e.preventDefault();
            
            const fileInput = document.getElementById('videoInput');
            const file = fileInput.files[0];
            
            if (!file) {
                alert('Please select a video file');
                return;
            }
            
            const formData = new FormData();
            formData.append('video', file);
            const modelChoice = document.querySelector('input[name="model_choice"]:checked').value;
            formData.append('model_choice', modelChoice);
            if (modelChoice === 'movenet') {
                const movenetVariant = document.querySelector('input[name="movenet_variant"]:checked').value;
                formData.append('movenet_variant', movenetVariant);
            }

            // Show loading
            document.getElementById('loading').classList.remove('hidden');
            document.getElementById('result').classList.add('hidden');
            
            try {
                const response = await fetch('/upload', {
                    method: 'POST',
                    body: formData
                });
                
                console.log('[CLIENT] Full response object from /upload:', response);
                console.log('[CLIENT] Response status from /upload:', response.status);
                console.log('[CLIENT] Response status text from /upload:', response.statusText);
                
                const data = await response.json();
                console.log('[CLIENT] Parsed JSON data from /upload:', data);
                
                if (!response.ok) {
                    throw new Error(data.error || 'Failed to process video');
                }
                
                // Create video element if it doesn't exist
                let videoElement = document.getElementById('outputVideo');
                if (!videoElement) {
                    videoElement = document.createElement('video');
                    videoElement.id = 'outputVideo';
                    videoElement.controls = true;
                    videoElement.style.width = '100%';
                    videoElement.style.maxWidth = '800px';
                    document.getElementById('result').appendChild(videoElement);
                }
                
                // Set up video source
                videoElement.src = data.output_path;
                
                // Wait for video to be loaded
                await new Promise((resolve, reject) => {
                    videoElement.onloadeddata = resolve;
                    videoElement.onerror = reject;
                    videoElement.load();
                });
                
                // Show result
                document.getElementById('loading').classList.add('hidden');
                document.getElementById('result').classList.remove('hidden');
                
            } catch (error) {
                console.error('[CLIENT] Error:', error);
                document.getElementById('loading').classList.add('hidden');
                alert('Error processing video: ' + error.message);
            }
        });
    </script>
</body>
</html>