frucht commited on
Commit
0dd54d2
·
verified ·
1 Parent(s): 2fce00a

Add 3 files

Browse files
Files changed (3) hide show
  1. README.md +7 -5
  2. index.html +1195 -19
  3. prompts.txt +3 -0
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
- title: Workout Archiver
3
- emoji: 🐢
4
- colorFrom: green
5
- colorTo: yellow
6
  sdk: static
7
  pinned: false
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
+ title: workout-archiver
3
+ emoji: 🐳
4
+ colorFrom: gray
5
+ colorTo: gray
6
  sdk: static
7
  pinned: false
8
+ tags:
9
+ - deepsite
10
  ---
11
 
12
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
index.html CHANGED
@@ -1,19 +1,1195 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en" class="dark">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Workout Video Archiver</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
9
+ <style>
10
+ .video-upload-area {
11
+ border: 2px dashed #ccc;
12
+ transition: all 0.3s;
13
+ }
14
+ .video-upload-area:hover {
15
+ border-color: #4f46e5;
16
+ background-color: rgba(79, 70, 229, 0.05);
17
+ }
18
+ .video-upload-area.dragover {
19
+ border-color: #4f46e5;
20
+ background-color: rgba(79, 70, 229, 0.1);
21
+ }
22
+ .video-thumbnail {
23
+ aspect-ratio: 16/9;
24
+ background-color: #f3f4f6;
25
+ transition: transform 0.2s;
26
+ }
27
+ .video-thumbnail:hover {
28
+ transform: scale(1.02);
29
+ }
30
+ .tag {
31
+ transition: all 0.2s;
32
+ }
33
+ .tag:hover {
34
+ transform: translateY(-1px);
35
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
36
+ }
37
+ .dark .video-thumbnail {
38
+ background-color: #1f2937;
39
+ }
40
+ .dark .tag {
41
+ background-color: #374151;
42
+ }
43
+ .dark .video-upload-area {
44
+ border-color: #4b5563;
45
+ }
46
+ .dark .video-upload-area:hover {
47
+ border-color: #818cf8;
48
+ background-color: rgba(129, 140, 248, 0.05);
49
+ }
50
+ .dark .video-upload-area.dragover {
51
+ border-color: #818cf8;
52
+ background-color: rgba(129, 140, 248, 0.1);
53
+ }
54
+ /* Custom scrollbar */
55
+ ::-webkit-scrollbar {
56
+ width: 8px;
57
+ height: 8px;
58
+ }
59
+ ::-webkit-scrollbar-track {
60
+ background: #f1f1f1;
61
+ }
62
+ ::-webkit-scrollbar-thumb {
63
+ background: #888;
64
+ border-radius: 4px;
65
+ }
66
+ ::-webkit-scrollbar-thumb:hover {
67
+ background: #555;
68
+ }
69
+ .dark ::-webkit-scrollbar-track {
70
+ background: #1f2937;
71
+ }
72
+ .dark ::-webkit-scrollbar-thumb {
73
+ background: #4b5563;
74
+ }
75
+ .dark ::-webkit-scrollbar-thumb:hover {
76
+ background: #6b7280;
77
+ }
78
+ /* Loading spinner */
79
+ .spinner {
80
+ width: 24px;
81
+ height: 24px;
82
+ border: 3px solid rgba(255,255,255,0.3);
83
+ border-radius: 50%;
84
+ border-top-color: #fff;
85
+ animation: spin 1s ease-in-out infinite;
86
+ }
87
+ @keyframes spin {
88
+ to { transform: rotate(360deg); }
89
+ }
90
+ </style>
91
+ </head>
92
+ <body class="bg-gray-100 dark:bg-gray-900 text-gray-800 dark:text-gray-200 transition-colors duration-200 min-h-screen">
93
+ <div class="container mx-auto px-4 py-6">
94
+ <!-- Header -->
95
+ <header class="flex justify-between items-center mb-8">
96
+ <div>
97
+ <h1 class="text-3xl font-bold text-indigo-600 dark:text-indigo-400">Workout Video Archiver</h1>
98
+ <p class="text-gray-600 dark:text-gray-400">Organize and share your training videos</p>
99
+ </div>
100
+ <div class="flex items-center space-x-4">
101
+ <button id="theme-toggle" class="p-2 rounded-full bg-gray-200 dark:bg-gray-700 hover:bg-gray-300 dark:hover:bg-gray-600">
102
+ <i class="fas fa-moon dark:hidden"></i>
103
+ <i class="fas fa-sun hidden dark:inline"></i>
104
+ </button>
105
+ <div id="profile-section">
106
+ <div class="relative">
107
+ <img id="profile-pic" src="https://randomuser.me/api/portraits/men/32.jpg" alt="Profile" class="w-10 h-10 rounded-full cursor-pointer">
108
+ <div class="absolute right-0 bottom-0 w-3 h-3 bg-green-500 rounded-full border-2 border-white dark:border-gray-900"></div>
109
+ </div>
110
+ </div>
111
+ </div>
112
+ </header>
113
+
114
+ <!-- Main Content -->
115
+ <div class="grid grid-cols-1 lg:grid-cols-4 gap-6">
116
+ <!-- Sidebar -->
117
+ <aside class="lg:col-span-1 space-y-6">
118
+ <!-- Navigation -->
119
+ <nav class="bg-white dark:bg-gray-800 rounded-lg shadow p-4">
120
+ <ul class="space-y-2">
121
+ <li>
122
+ <a href="#" class="flex items-center space-x-3 p-2 rounded-lg bg-indigo-50 dark:bg-indigo-900/30 text-indigo-700 dark:text-indigo-300">
123
+ <i class="fas fa-home w-5"></i>
124
+ <span>Dashboard</span>
125
+ </a>
126
+ </li>
127
+ <li>
128
+ <a href="#" class="flex items-center space-x-3 p-2 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-700">
129
+ <i class="fas fa-video w-5"></i>
130
+ <span>My Videos</span>
131
+ </a>
132
+ </li>
133
+ <li>
134
+ <a href="#" class="flex items-center space-x-3 p-2 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-700">
135
+ <i class="fas fa-tags w-5"></i>
136
+ <span>Tags</span>
137
+ </a>
138
+ </li>
139
+ <li>
140
+ <a href="#" class="flex items-center space-x-3 p-2 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-700">
141
+ <i class="fas fa-chart-line w-5"></i>
142
+ <span>Analytics</span>
143
+ </a>
144
+ </li>
145
+ <li>
146
+ <a href="#" class="flex items-center space-x-3 p-2 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-700">
147
+ <i class="fas fa-cog w-5"></i>
148
+ <span>Settings</span>
149
+ </a>
150
+ </li>
151
+ </ul>
152
+ </nav>
153
+
154
+ <!-- Quick Stats -->
155
+ <div class="bg-white dark:bg-gray-800 rounded-lg shadow p-4">
156
+ <h3 class="font-medium mb-3">Quick Stats</h3>
157
+ <div class="space-y-3">
158
+ <div>
159
+ <p class="text-sm text-gray-500 dark:text-gray-400">Total Videos</p>
160
+ <p id="total-videos" class="text-xl font-semibold">0</p>
161
+ </div>
162
+ <div>
163
+ <p class="text-sm text-gray-500 dark:text-gray-400">Workouts</p>
164
+ <p id="total-workouts" class="text-xl font-semibold">0</p>
165
+ </div>
166
+ <div>
167
+ <p class="text-sm text-gray-500 dark:text-gray-400">Tags</p>
168
+ <p id="total-tags" class="text-xl font-semibold">0</p>
169
+ </div>
170
+ </div>
171
+ </div>
172
+
173
+ <!-- Recent Tags -->
174
+ <div class="bg-white dark:bg-gray-800 rounded-lg shadow p-4">
175
+ <h3 class="font-medium mb-3">Recent Tags</h3>
176
+ <div id="recent-tags" class="flex flex-wrap gap-2">
177
+ <!-- Tags will be loaded here -->
178
+ </div>
179
+ </div>
180
+ </aside>
181
+
182
+ <!-- Main Area -->
183
+ <main class="lg:col-span-3 space-y-6">
184
+ <!-- Upload Section -->
185
+ <section class="bg-white dark:bg-gray-800 rounded-lg shadow p-6">
186
+ <div class="flex justify-between items-center mb-4">
187
+ <h2 class="text-xl font-semibold">Upload New Video</h2>
188
+ <div class="flex space-x-2">
189
+ <button id="record-btn" class="px-4 py-2 bg-indigo-600 hover:bg-indigo-700 text-white rounded-lg flex items-center space-x-2">
190
+ <i class="fas fa-video"></i>
191
+ <span>Record</span>
192
+ </button>
193
+ <button id="upload-btn" class="px-4 py-2 bg-gray-200 dark:bg-gray-700 hover:bg-gray-300 dark:hover:bg-gray-600 rounded-lg flex items-center space-x-2">
194
+ <i class="fas fa-upload"></i>
195
+ <span>Upload</span>
196
+ </button>
197
+ </div>
198
+ </div>
199
+
200
+ <div id="upload-area" class="video-upload-area rounded-lg p-8 text-center cursor-pointer">
201
+ <i class="fas fa-cloud-upload-alt text-4xl text-gray-400 mb-3"></i>
202
+ <p class="font-medium">Drag & drop your video here</p>
203
+ <p class="text-sm text-gray-500 dark:text-gray-400 mt-1">or click to browse files</p>
204
+ <input type="file" id="file-input" accept="video/*" class="hidden">
205
+ </div>
206
+ </section>
207
+
208
+ <!-- Filters -->
209
+ <section class="bg-white dark:bg-gray-800 rounded-lg shadow p-6">
210
+ <div class="flex flex-col md:flex-row md:items-center md:justify-between gap-4 mb-4">
211
+ <h2 class="text-xl font-semibold">My Videos</h2>
212
+ <div class="flex flex-col sm:flex-row gap-3">
213
+ <div class="relative">
214
+ <select id="sort-select" class="appearance-none bg-gray-100 dark:bg-gray-700 border-0 rounded-lg pl-4 pr-8 py-2 focus:ring-2 focus:ring-indigo-500 focus:outline-none">
215
+ <option value="date">Sort by Date</option>
216
+ <option value="name">Sort by Name</option>
217
+ <option value="workout">Sort by Workout</option>
218
+ </select>
219
+ <i class="fas fa-chevron-down absolute right-3 top-3 text-gray-500 pointer-events-none"></i>
220
+ </div>
221
+ <div class="relative">
222
+ <select id="workout-filter" class="appearance-none bg-gray-100 dark:bg-gray-700 border-0 rounded-lg pl-4 pr-8 py-2 focus:ring-2 focus:ring-indigo-500 focus:outline-none">
223
+ <option value="all">All Workouts</option>
224
+ <option value="strength">Strength Training</option>
225
+ <option value="hiit">HIIT</option>
226
+ <option value="yoga">Yoga</option>
227
+ <option value="cardio">Cardio</option>
228
+ </select>
229
+ <i class="fas fa-chevron-down absolute right-3 top-3 text-gray-500 pointer-events-none"></i>
230
+ </div>
231
+ <div class="relative">
232
+ <input id="search-input" type="text" placeholder="Search videos..." class="bg-gray-100 dark:bg-gray-700 border-0 rounded-lg pl-4 pr-10 py-2 focus:ring-2 focus:ring-indigo-500 focus:outline-none">
233
+ <i class="fas fa-search absolute right-3 top-3 text-gray-500 pointer-events-none"></i>
234
+ </div>
235
+ </div>
236
+ </div>
237
+
238
+ <div id="active-filters" class="flex flex-wrap gap-3 mb-4">
239
+ <!-- Active filters will appear here -->
240
+ </div>
241
+ </section>
242
+
243
+ <!-- Video Grid -->
244
+ <section id="video-grid" class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
245
+ <!-- Videos will be loaded here -->
246
+ <div id="loading-spinner" class="col-span-full flex justify-center py-8 hidden">
247
+ <div class="spinner"></div>
248
+ </div>
249
+ <div id="no-videos" class="col-span-full text-center py-8 text-gray-500 dark:text-gray-400">
250
+ <i class="fas fa-video-slash text-4xl mb-3"></i>
251
+ <p class="text-lg">No videos found</p>
252
+ <p class="text-sm">Upload your first workout video to get started</p>
253
+ </div>
254
+ </section>
255
+ </main>
256
+ </div>
257
+ </div>
258
+
259
+ <!-- Video Modal -->
260
+ <div id="video-modal" class="fixed inset-0 bg-black bg-opacity-75 flex items-center justify-center z-50 hidden">
261
+ <div class="bg-white dark:bg-gray-800 rounded-lg w-full max-w-4xl mx-4 relative">
262
+ <button id="close-modal" class="absolute -top-10 right-0 text-white hover:text-gray-300">
263
+ <i class="fas fa-times text-2xl"></i>
264
+ </button>
265
+ <div class="p-4">
266
+ <video id="modal-video" controls class="w-full rounded-lg">
267
+ Your browser does not support the video tag.
268
+ </video>
269
+ </div>
270
+ <div class="p-6">
271
+ <h3 id="modal-title" class="text-xl font-semibold mb-2"></h3>
272
+ <p id="modal-description" class="text-gray-600 dark:text-gray-400 mb-4"></p>
273
+ <div class="flex flex-wrap gap-2 mb-4" id="modal-tags"></div>
274
+ <div class="flex justify-between items-center">
275
+ <div class="text-sm text-gray-500 dark:text-gray-400">
276
+ <span id="modal-date"></span> • <span id="modal-duration"></span>
277
+ </div>
278
+ <div class="flex space-x-3">
279
+ <button id="whatsapp-share" class="px-4 py-2 bg-indigo-600 hover:bg-indigo-700 text-white rounded-lg flex items-center space-x-2">
280
+ <i class="fab fa-whatsapp"></i>
281
+ <span>WhatsApp</span>
282
+ </button>
283
+ <button id="email-share" class="px-4 py-2 bg-gray-200 dark:bg-gray-700 hover:bg-gray-300 dark:hover:bg-gray-600 rounded-lg flex items-center space-x-2">
284
+ <i class="fas fa-envelope"></i>
285
+ <span>Email</span>
286
+ </button>
287
+ <button id="copy-link" class="px-4 py-2 bg-gray-200 dark:bg-gray-700 hover:bg-gray-300 dark:hover:bg-gray-600 rounded-lg flex items-center space-x-2">
288
+ <i class="fas fa-link"></i>
289
+ <span>Copy Link</span>
290
+ </button>
291
+ </div>
292
+ </div>
293
+ </div>
294
+ </div>
295
+ </div>
296
+
297
+ <!-- Upload Modal -->
298
+ <div id="upload-modal" class="fixed inset-0 bg-black bg-opacity-75 flex items-center justify-center z-50 hidden">
299
+ <div class="bg-white dark:bg-gray-800 rounded-lg w-full max-w-2xl mx-4 relative">
300
+ <button id="close-upload-modal" class="absolute -top-10 right-0 text-white hover:text-gray-300">
301
+ <i class="fas fa-times text-2xl"></i>
302
+ </button>
303
+ <div class="p-6">
304
+ <h3 class="text-xl font-semibold mb-4">Upload New Workout Video</h3>
305
+
306
+ <div class="mb-6">
307
+ <label class="block text-sm font-medium mb-2">Video File</label>
308
+ <div id="modal-upload-area" class="video-upload-area rounded-lg p-8 text-center cursor-pointer mb-2">
309
+ <i class="fas fa-cloud-upload-alt text-4xl text-gray-400 mb-3"></i>
310
+ <p class="font-medium">Drag & drop your video here</p>
311
+ <p class="text-sm text-gray-500 dark:text-gray-400 mt-1">or click to browse files</p>
312
+ <input type="file" id="modal-file-input" accept="video/*" class="hidden">
313
+ </div>
314
+ <p class="text-xs text-gray-500 dark:text-gray-400">MP4, MOV or AVI. Max 500MB.</p>
315
+ </div>
316
+
317
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-6">
318
+ <div>
319
+ <label for="video-title" class="block text-sm font-medium mb-2">Video Title</label>
320
+ <input type="text" id="video-title" class="w-full bg-gray-100 dark:bg-gray-700 border-0 rounded-lg px-4 py-2 focus:ring-2 focus:ring-indigo-500 focus:outline-none">
321
+ </div>
322
+ <div>
323
+ <label for="workout-name" class="block text-sm font-medium mb-2">Workout Name</label>
324
+ <input type="text" id="workout-name" class="w-full bg-gray-100 dark:bg-gray-700 border-0 rounded-lg px-4 py-2 focus:ring-2 focus:ring-indigo-500 focus:outline-none">
325
+ </div>
326
+ <div>
327
+ <label for="training-name" class="block text-sm font-medium mb-2">Training Program</label>
328
+ <input type="text" id="training-name" class="w-full bg-gray-100 dark:bg-gray-700 border-0 rounded-lg px-4 py-2 focus:ring-2 focus:ring-indigo-500 focus:outline-none">
329
+ </div>
330
+ <div>
331
+ <label for="video-date" class="block text-sm font-medium mb-2">Date</label>
332
+ <input type="date" id="video-date" class="w-full bg-gray-100 dark:bg-gray-700 border-0 rounded-lg px-4 py-2 focus:ring-2 focus:ring-indigo-500 focus:outline-none">
333
+ </div>
334
+ </div>
335
+
336
+ <div class="mb-6">
337
+ <label for="video-description" class="block text-sm font-medium mb-2">Description</label>
338
+ <textarea id="video-description" rows="3" class="w-full bg-gray-100 dark:bg-gray-700 border-0 rounded-lg px-4 py-2 focus:ring-2 focus:ring-indigo-500 focus:outline-none"></textarea>
339
+ </div>
340
+
341
+ <div class="mb-6">
342
+ <label class="block text-sm font-medium mb-2">Tags</label>
343
+ <div class="flex flex-wrap gap-2 mb-2" id="selected-tags">
344
+ <!-- Tags will be added here -->
345
+ </div>
346
+ <div class="flex">
347
+ <input type="text" id="tag-input" placeholder="Add a tag" class="flex-1 bg-gray-100 dark:bg-gray-700 border-0 rounded-l-lg px-4 py-2 focus:ring-2 focus:ring-indigo-500 focus:outline-none">
348
+ <button id="add-tag-btn" class="px-4 bg-indigo-600 text-white rounded-r-lg hover:bg-indigo-700">Add</button>
349
+ </div>
350
+ <div class="flex flex-wrap gap-2 mt-2">
351
+ <span class="tag px-3 py-1 bg-gray-100 dark:bg-gray-700 rounded-full text-xs cursor-pointer">strength</span>
352
+ <span class="tag px-3 py-1 bg-gray-100 dark:bg-gray-700 rounded-full text-xs cursor-pointer">hiit</span>
353
+ <span class="tag px-3 py-1 bg-gray-100 dark:bg-gray-700 rounded-full text-xs cursor-pointer">beginner</span>
354
+ <span class="tag px-3 py-1 bg-gray-100 dark:bg-gray-700 rounded-full text-xs cursor-pointer">advanced</span>
355
+ <span class="tag px-3 py-1 bg-gray-100 dark:bg-gray-700 rounded-full text-xs cursor-pointer">core</span>
356
+ <span class="tag px-3 py-1 bg-gray-100 dark:bg-gray-700 rounded-full text-xs cursor-pointer">upper body</span>
357
+ </div>
358
+ </div>
359
+
360
+ <div class="flex justify-end space-x-3">
361
+ <button id="cancel-upload" class="px-4 py-2 bg-gray-200 dark:bg-gray-700 hover:bg-gray-300 dark:hover:bg-gray-600 rounded-lg">
362
+ Cancel
363
+ </button>
364
+ <button id="save-video" class="px-4 py-2 bg-indigo-600 hover:bg-indigo-700 text-white rounded-lg flex items-center space-x-2">
365
+ <i class="fas fa-save"></i>
366
+ <span>Save Video</span>
367
+ </button>
368
+ </div>
369
+ </div>
370
+ </div>
371
+ </div>
372
+
373
+ <!-- Loading Modal -->
374
+ <div id="loading-modal" class="fixed inset-0 bg-black bg-opacity-75 flex items-center justify-center z-50 hidden">
375
+ <div class="bg-white dark:bg-gray-800 rounded-lg p-8 text-center">
376
+ <div class="spinner mx-auto mb-4"></div>
377
+ <p id="loading-text" class="text-lg font-medium">Uploading video...</p>
378
+ </div>
379
+ </div>
380
+
381
+ <script>
382
+ // Global variables
383
+ let videos = [];
384
+ let selectedFile = null;
385
+
386
+ // DOM elements
387
+ const videoGrid = document.getElementById('video-grid');
388
+ const loadingSpinner = document.getElementById('loading-spinner');
389
+ const noVideos = document.getElementById('no-videos');
390
+ const totalVideos = document.getElementById('total-videos');
391
+ const totalWorkouts = document.getElementById('total-workouts');
392
+ const totalTags = document.getElementById('total-tags');
393
+ const recentTags = document.getElementById('recent-tags');
394
+ const activeFilters = document.getElementById('active-filters');
395
+ const sortSelect = document.getElementById('sort-select');
396
+ const workoutFilter = document.getElementById('workout-filter');
397
+ const searchInput = document.getElementById('search-input');
398
+
399
+ // Load videos from localStorage
400
+ function loadVideos() {
401
+ try {
402
+ loadingSpinner.classList.remove('hidden');
403
+ noVideos.classList.add('hidden');
404
+
405
+ // Load from localStorage
406
+ const savedVideos = localStorage.getItem('workoutVideos');
407
+ if (savedVideos) {
408
+ videos = JSON.parse(savedVideos);
409
+ } else {
410
+ // Default demo videos
411
+ videos = [
412
+ {
413
+ id: '1',
414
+ title: "Full Body Strength Workout - Week 1",
415
+ description: "Complete full body strength workout focusing on compound movements. Perfect for beginners starting their strength journey.",
416
+ tags: ["strength", "beginner", "full body"],
417
+ date: "2 days ago",
418
+ duration: "4:32",
419
+ thumbnail: "https://images.unsplash.com/photo-1571019613454-1cb2f99b2d8b?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=800&q=80",
420
+ videoUrl: "https://sample-videos.com/video123/mp4/720/big_buck_bunny_720p_1mb.mp4",
421
+ workoutType: "strength",
422
+ uploadDate: new Date(Date.now() - 2 * 24 * 60 * 60 * 1000).toISOString()
423
+ },
424
+ {
425
+ id: '2',
426
+ title: "Advanced HIIT Circuit - Fat Burning",
427
+ description: "High intensity interval training circuit designed to maximize fat burning and improve cardiovascular endurance.",
428
+ tags: ["hiit", "advanced", "cardio", "fat loss"],
429
+ date: "1 week ago",
430
+ duration: "12:15",
431
+ thumbnail: "https://images.unsplash.com/photo-1571019614242-c5c5a73d6587?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=800&q=80",
432
+ videoUrl: "https://sample-videos.com/video123/mp4/720/big_buck_bunny_720p_1mb.mp4",
433
+ workoutType: "hiit",
434
+ uploadDate: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString()
435
+ },
436
+ {
437
+ id: '3',
438
+ title: "Morning Yoga Flow - Flexibility Routine",
439
+ description: "Gentle yoga flow perfect for mornings to improve flexibility, mobility and start your day with energy.",
440
+ tags: ["yoga", "flexibility", "morning"],
441
+ date: "2 weeks ago",
442
+ duration: "8:45",
443
+ thumbnail: "https://images.unsplash.com/photo-1545205597-3d9d02c29597?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=800&q=80",
444
+ videoUrl: "https://sample-videos.com/video123/mp4/720/big_buck_bunny_720p_1mb.mp4",
445
+ workoutType: "yoga",
446
+ uploadDate: new Date(Date.now() - 14 * 24 * 60 * 60 * 1000).toISOString()
447
+ }
448
+ ];
449
+ // Save to localStorage
450
+ localStorage.setItem('workoutVideos', JSON.stringify(videos));
451
+ }
452
+
453
+ updateStats();
454
+ renderVideos();
455
+ loadingSpinner.classList.add('hidden');
456
+
457
+ if (videos.length === 0) {
458
+ noVideos.classList.remove('hidden');
459
+ noVideos.innerHTML = `
460
+ <i class="fas fa-video-slash text-4xl mb-3"></i>
461
+ <p class="text-lg">No videos found</p>
462
+ <p class="text-sm">Upload your first workout video to get started</p>
463
+ `;
464
+ }
465
+ } catch (error) {
466
+ console.error('Error loading videos:', error);
467
+ loadingSpinner.classList.add('hidden');
468
+ alert('Failed to load videos. Please try again.');
469
+ }
470
+ }
471
+
472
+ // Save videos to localStorage
473
+ function saveVideosToStorage() {
474
+ localStorage.setItem('workoutVideos', JSON.stringify(videos));
475
+ }
476
+
477
+ // Update statistics
478
+ function updateStats() {
479
+ totalVideos.textContent = videos.length;
480
+
481
+ // Count unique workout types
482
+ const workoutTypes = new Set(videos.map(video => video.workoutType));
483
+ totalWorkouts.textContent = workoutTypes.size;
484
+
485
+ // Count unique tags
486
+ const allTags = videos.flatMap(video => video.tags);
487
+ const uniqueTags = new Set(allTags);
488
+ totalTags.textContent = uniqueTags.size;
489
+
490
+ // Update recent tags
491
+ recentTags.innerHTML = '';
492
+ const tagCounts = {};
493
+ allTags.forEach(tag => {
494
+ tagCounts[tag] = (tagCounts[tag] || 0) + 1;
495
+ });
496
+
497
+ const sortedTags = Object.keys(tagCounts).sort((a, b) => tagCounts[b] - tagCounts[a]);
498
+ sortedTags.slice(0, 6).forEach(tag => {
499
+ const tagElement = document.createElement('span');
500
+ tagElement.className = 'tag px-3 py-1 bg-gray-100 dark:bg-gray-700 rounded-full text-sm';
501
+ tagElement.textContent = tag;
502
+ recentTags.appendChild(tagElement);
503
+ });
504
+ }
505
+
506
+ // Render videos to the grid
507
+ function renderVideos() {
508
+ videoGrid.innerHTML = '';
509
+
510
+ // Filter and sort videos
511
+ let filteredVideos = [...videos];
512
+
513
+ // Apply search filter
514
+ const searchTerm = searchInput.value.toLowerCase();
515
+ if (searchTerm) {
516
+ filteredVideos = filteredVideos.filter(video =>
517
+ video.title.toLowerCase().includes(searchTerm) ||
518
+ video.description.toLowerCase().includes(searchTerm) ||
519
+ video.tags.some(tag => tag.toLowerCase().includes(searchTerm))
520
+ );
521
+ }
522
+
523
+ // Apply workout type filter
524
+ const workoutType = workoutFilter.value;
525
+ if (workoutType !== 'all') {
526
+ filteredVideos = filteredVideos.filter(video => video.workoutType === workoutType);
527
+ }
528
+
529
+ // Apply sorting
530
+ const sortBy = sortSelect.value;
531
+ if (sortBy === 'date') {
532
+ filteredVideos.sort((a, b) => new Date(b.uploadDate) - new Date(a.uploadDate));
533
+ } else if (sortBy === 'name') {
534
+ filteredVideos.sort((a, b) => a.title.localeCompare(b.title));
535
+ } else if (sortBy === 'workout') {
536
+ filteredVideos.sort((a, b) => a.workoutType.localeCompare(b.workoutType));
537
+ }
538
+
539
+ // Update active filters
540
+ updateActiveFilters();
541
+
542
+ // Render videos
543
+ if (filteredVideos.length === 0) {
544
+ noVideos.classList.remove('hidden');
545
+ } else {
546
+ noVideos.classList.add('hidden');
547
+ filteredVideos.forEach((video, index) => {
548
+ const videoCard = createVideoCard(video, index);
549
+ videoGrid.appendChild(videoCard);
550
+ });
551
+ }
552
+
553
+ // Add "Add New Video" card
554
+ const addCard = document.createElement('div');
555
+ addCard.className = 'bg-white dark:bg-gray-800 rounded-lg shadow overflow-hidden hover:shadow-lg transition-shadow flex items-center justify-center';
556
+ addCard.innerHTML = `
557
+ <button class="w-full h-full p-8 flex flex-col items-center justify-center text-indigo-600 dark:text-indigo-400 hover:text-indigo-800 dark:hover:text-indigo-300">
558
+ <i class="fas fa-plus-circle text-4xl mb-3"></i>
559
+ <span class="font-medium">Add New Video</span>
560
+ </button>
561
+ `;
562
+ addCard.querySelector('button').addEventListener('click', () => {
563
+ document.getElementById('upload-modal').classList.remove('hidden');
564
+ document.body.style.overflow = 'hidden';
565
+ });
566
+ videoGrid.appendChild(addCard);
567
+ }
568
+
569
+ // Create a video card element
570
+ function createVideoCard(video, index) {
571
+ const card = document.createElement('div');
572
+ card.className = 'bg-white dark:bg-gray-800 rounded-lg shadow overflow-hidden hover:shadow-lg transition-shadow';
573
+ card.innerHTML = `
574
+ <div class="video-thumbnail relative">
575
+ <img src="${video.thumbnail}" alt="Workout Video" class="w-full h-full object-cover">
576
+ <div class="absolute inset-0 bg-black bg-opacity-30 flex items-center justify-center opacity-0 hover:opacity-100 transition-opacity">
577
+ <button class="w-12 h-12 bg-white bg-opacity-80 rounded-full flex items-center justify-center text-indigo-600 hover:bg-opacity-100">
578
+ <i class="fas fa-play"></i>
579
+ </button>
580
+ </div>
581
+ <div class="absolute bottom-2 right-2 bg-black bg-opacity-70 text-white text-xs px-2 py-1 rounded">
582
+ ${video.duration}
583
+ </div>
584
+ </div>
585
+ <div class="p-4">
586
+ <div class="flex justify-between items-start mb-2">
587
+ <h3 class="font-semibold line-clamp-1">${video.title}</h3>
588
+ <div class="dropdown relative">
589
+ <button class="text-gray-500 hover:text-gray-700 dark:hover:text-gray-300">
590
+ <i class="fas fa-ellipsis-v"></i>
591
+ </button>
592
+ <div class="dropdown-menu absolute right-0 mt-2 w-48 bg-white dark:bg-gray-800 rounded-md shadow-lg z-10 hidden">
593
+ <div class="py-1">
594
+ <a href="#" class="block px-4 py-2 text-sm text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700">Edit</a>
595
+ <a href="#" class="block px-4 py-2 text-sm text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700">Share</a>
596
+ <a href="#" class="block px-4 py-2 text-sm text-red-600 hover:bg-gray-100 dark:hover:bg-gray-700">Delete</a>
597
+ </div>
598
+ </div>
599
+ </div>
600
+ </div>
601
+ <p class="text-sm text-gray-600 dark:text-gray-400 mb-3 line-clamp-2">${video.description}</p>
602
+ <div class="flex flex-wrap gap-2 mb-3">
603
+ ${video.tags.map(tag => `<span class="tag px-2 py-1 bg-gray-100 dark:bg-gray-700 rounded-full text-xs">${tag}</span>`).join('')}
604
+ </div>
605
+ <div class="flex justify-between items-center text-xs text-gray-500 dark:text-gray-400">
606
+ <span>${video.date}</span>
607
+ <div class="flex space-x-2">
608
+ <button class="hover:text-indigo-600 dark:hover:text-indigo-400">
609
+ <i class="fas fa-share-alt"></i>
610
+ </button>
611
+ <button class="hover:text-indigo-600 dark:hover:text-indigo-400">
612
+ <i class="fas fa-download"></i>
613
+ </button>
614
+ </div>
615
+ </div>
616
+ </div>
617
+ `;
618
+
619
+ // Add click handler for play button
620
+ card.querySelector('.video-thumbnail button').addEventListener('click', () => {
621
+ openVideoModal(video);
622
+ });
623
+
624
+ // Add dropdown menu functionality
625
+ const dropdownBtn = card.querySelector('.dropdown button');
626
+ const dropdownMenu = card.querySelector('.dropdown-menu');
627
+
628
+ dropdownBtn.addEventListener('click', (e) => {
629
+ e.stopPropagation();
630
+ dropdownMenu.classList.toggle('hidden');
631
+ });
632
+
633
+ // Add click handler for edit button
634
+ const editBtn = card.querySelector('.dropdown-menu a:first-child');
635
+ editBtn.addEventListener('click', (e) => {
636
+ e.preventDefault();
637
+ openEditModal(video);
638
+ });
639
+
640
+ // Add click handler for delete button
641
+ const deleteBtn = card.querySelector('.dropdown-menu a:last-child');
642
+ deleteBtn.addEventListener('click', (e) => {
643
+ e.preventDefault();
644
+ if (confirm('Are you sure you want to delete this video?')) {
645
+ deleteVideo(video.id);
646
+ }
647
+ });
648
+
649
+ return card;
650
+ }
651
+
652
+ // Update active filters display
653
+ function updateActiveFilters() {
654
+ activeFilters.innerHTML = '';
655
+
656
+ // Add search filter if applicable
657
+ const searchTerm = searchInput.value.trim();
658
+ if (searchTerm) {
659
+ const filter = document.createElement('span');
660
+ filter.className = 'tag px-3 py-1 bg-indigo-100 dark:bg-indigo-900/30 text-indigo-700 dark:text-indigo-300 rounded-full text-sm flex items-center';
661
+ filter.innerHTML = `
662
+ <span>Search: "${searchTerm}"</span>
663
+ <button class="ml-2 text-indigo-500 dark:text-indigo-400 hover:text-indigo-700 dark:hover:text-indigo-300">
664
+ <i class="fas fa-times"></i>
665
+ </button>
666
+ `;
667
+ filter.querySelector('button').addEventListener('click', () => {
668
+ searchInput.value = '';
669
+ renderVideos();
670
+ });
671
+ activeFilters.appendChild(filter);
672
+ }
673
+
674
+ // Add workout type filter if applicable
675
+ const workoutType = workoutFilter.value;
676
+ if (workoutType !== 'all') {
677
+ const filter = document.createElement('span');
678
+ filter.className = 'tag px-3 py-1 bg-indigo-100 dark:bg-indigo-900/30 text-indigo-700 dark:text-indigo-300 rounded-full text-sm flex items-center';
679
+ filter.innerHTML = `
680
+ <span>Workout: ${workoutType.charAt(0).toUpperCase() + workoutType.slice(1)}</span>
681
+ <button class="ml-2 text-indigo-500 dark:text-indigo-400 hover:text-indigo-700 dark:hover:text-indigo-300">
682
+ <i class="fas fa-times"></i>
683
+ </button>
684
+ `;
685
+ filter.querySelector('button').addEventListener('click', () => {
686
+ workoutFilter.value = 'all';
687
+ renderVideos();
688
+ });
689
+ activeFilters.appendChild(filter);
690
+ }
691
+
692
+ // Add sort filter if applicable
693
+ const sortBy = sortSelect.value;
694
+ if (sortBy !== 'date') {
695
+ const filter = document.createElement('span');
696
+ filter.className = 'tag px-3 py-1 bg-indigo-100 dark:bg-indigo-900/30 text-indigo-700 dark:text-indigo-300 rounded-full text-sm flex items-center';
697
+ filter.innerHTML = `
698
+ <span>Sorted by: ${sortBy.charAt(0).toUpperCase() + sortBy.slice(1)}</span>
699
+ <button class="ml-2 text-indigo-500 dark:text-indigo-400 hover:text-indigo-700 dark:hover:text-indigo-300">
700
+ <i class="fas fa-times"></i>
701
+ </button>
702
+ `;
703
+ filter.querySelector('button').addEventListener('click', () => {
704
+ sortSelect.value = 'date';
705
+ renderVideos();
706
+ });
707
+ activeFilters.appendChild(filter);
708
+ }
709
+
710
+ // Add "Clear All" button if there are active filters
711
+ if (activeFilters.children.length > 0) {
712
+ const clearAll = document.createElement('button');
713
+ clearAll.className = 'text-sm text-indigo-600 dark:text-indigo-400 hover:text-indigo-800 dark:hover:text-indigo-300 flex items-center';
714
+ clearAll.innerHTML = `
715
+ <i class="fas fa-times mr-1"></i>
716
+ <span>Clear All</span>
717
+ `;
718
+ clearAll.addEventListener('click', () => {
719
+ searchInput.value = '';
720
+ workoutFilter.value = 'all';
721
+ sortSelect.value = 'date';
722
+ renderVideos();
723
+ });
724
+ activeFilters.appendChild(clearAll);
725
+ }
726
+ }
727
+
728
+ // Open video modal
729
+ function openVideoModal(video) {
730
+ const videoModal = document.getElementById('video-modal');
731
+ const modalVideo = document.getElementById('modal-video');
732
+ const modalTitle = document.getElementById('modal-title');
733
+ const modalDescription = document.getElementById('modal-description');
734
+ const modalTags = document.getElementById('modal-tags');
735
+ const modalDate = document.getElementById('modal-date');
736
+ const modalDuration = document.getElementById('modal-duration');
737
+
738
+ modalVideo.src = video.videoUrl;
739
+ modalTitle.textContent = video.title;
740
+ modalDescription.textContent = video.description;
741
+ modalDate.textContent = video.date;
742
+ modalDuration.textContent = video.duration;
743
+
744
+ // Clear previous tags
745
+ modalTags.innerHTML = '';
746
+
747
+ // Add new tags
748
+ video.tags.forEach(tag => {
749
+ const tagElement = document.createElement('span');
750
+ tagElement.className = 'tag px-2 py-1 bg-gray-100 dark:bg-gray-700 rounded-full text-xs';
751
+ tagElement.textContent = tag;
752
+ modalTags.appendChild(tagElement);
753
+ });
754
+
755
+ // Add share button handlers
756
+ document.getElementById('whatsapp-share').addEventListener('click', () => {
757
+ shareVideo(video, 'whatsapp');
758
+ });
759
+
760
+ document.getElementById('email-share').addEventListener('click', () => {
761
+ shareVideo(video, 'email');
762
+ });
763
+
764
+ document.getElementById('copy-link').addEventListener('click', () => {
765
+ shareVideo(video, 'link');
766
+ });
767
+
768
+ videoModal.classList.remove('hidden');
769
+ document.body.style.overflow = 'hidden';
770
+ }
771
+
772
+ // Share video
773
+ function shareVideo(video, method) {
774
+ let message = `Check out my workout video: ${video.title}\n\n${video.description}\n\nTags: ${video.tags.join(', ')}`;
775
+
776
+ switch (method) {
777
+ case 'whatsapp':
778
+ window.open(`https://wa.me/?text=${encodeURIComponent(message)}`);
779
+ break;
780
+ case 'email':
781
+ window.open(`mailto:?subject=${encodeURIComponent(video.title)}&body=${encodeURIComponent(message)}`);
782
+ break;
783
+ case 'link':
784
+ navigator.clipboard.writeText(video.videoUrl).then(() => {
785
+ alert('Video link copied to clipboard!');
786
+ });
787
+ break;
788
+ }
789
+ }
790
+
791
+ // Open edit modal
792
+ function openEditModal(video) {
793
+ const uploadModal = document.getElementById('upload-modal');
794
+ const modalTitle = uploadModal.querySelector('h3');
795
+ const videoTitle = document.getElementById('video-title');
796
+ const workoutName = document.getElementById('workout-name');
797
+ const trainingName = document.getElementById('training-name');
798
+ const videoDate = document.getElementById('video-date');
799
+ const videoDescription = document.getElementById('video-description');
800
+ const selectedTags = document.getElementById('selected-tags');
801
+ const modalUploadArea = document.getElementById('modal-upload-area');
802
+
803
+ modalTitle.textContent = 'Edit Video';
804
+ videoTitle.value = video.title;
805
+ workoutName.value = video.workoutType;
806
+ trainingName.value = '';
807
+ videoDate.value = '';
808
+ videoDescription.value = video.description;
809
+
810
+ // Clear previous tags
811
+ selectedTags.innerHTML = '';
812
+
813
+ // Add current tags
814
+ video.tags.forEach(tag => {
815
+ addTagToForm(tag);
816
+ });
817
+
818
+ // Update upload area to show current video
819
+ modalUploadArea.innerHTML = `
820
+ <i class="fas fa-check-circle text-4xl text-green-500 mb-3"></i>
821
+ <p class="font-medium">Current Video: ${video.title}</p>
822
+ <p class="text-sm text-gray-500 dark:text-gray-400 mt-1">Click to change file</p>
823
+ `;
824
+
825
+ // Store the video being edited
826
+ selectedFile = { name: video.title };
827
+
828
+ uploadModal.classList.remove('hidden');
829
+ document.body.style.overflow = 'hidden';
830
+ }
831
+
832
+ // Delete video
833
+ function deleteVideo(videoId) {
834
+ videos = videos.filter(video => video.id !== videoId);
835
+ saveVideosToStorage();
836
+ updateStats();
837
+ renderVideos();
838
+ }
839
+
840
+ // Upload video (save to localStorage)
841
+ async function uploadVideo(file, metadata) {
842
+ try {
843
+ // Show loading modal
844
+ const loadingModal = document.getElementById('loading-modal');
845
+ const loadingText = document.getElementById('loading-text');
846
+ loadingText.textContent = 'Uploading video...';
847
+ loadingModal.classList.remove('hidden');
848
+
849
+ // Simulate upload delay
850
+ await new Promise(resolve => setTimeout(resolve, 1000));
851
+
852
+ // Create a new video object
853
+ const newVideo = {
854
+ id: Date.now().toString(),
855
+ title: metadata.title,
856
+ description: metadata.description,
857
+ tags: metadata.tags,
858
+ date: "Just now",
859
+ duration: "5:00", // In a real app, you would get this from the video
860
+ thumbnail: "https://images.unsplash.com/photo-1571019613454-1cb2f99b2d8b?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=800&q=80",
861
+ videoUrl: URL.createObjectURL(file),
862
+ workoutType: metadata.workoutType,
863
+ uploadDate: new Date().toISOString()
864
+ };
865
+
866
+ // Add to videos array
867
+ videos.unshift(newVideo);
868
+
869
+ // Save to localStorage
870
+ saveVideosToStorage();
871
+
872
+ // Update UI
873
+ updateStats();
874
+ renderVideos();
875
+
876
+ // Hide loading modal
877
+ loadingModal.classList.add('hidden');
878
+
879
+ // Show success message
880
+ alert('Video uploaded successfully!');
881
+
882
+ // Close upload modal
883
+ document.getElementById('upload-modal').classList.add('hidden');
884
+ document.body.style.overflow = 'auto';
885
+
886
+ // Reset form
887
+ resetUploadForm();
888
+ } catch (error) {
889
+ console.error('Error uploading video:', error);
890
+ loadingModal.classList.add('hidden');
891
+ alert('Failed to upload video. Please try again.');
892
+ }
893
+ }
894
+
895
+ // Reset upload form
896
+ function resetUploadForm() {
897
+ document.getElementById('video-title').value = '';
898
+ document.getElementById('workout-name').value = '';
899
+ document.getElementById('training-name').value = '';
900
+ document.getElementById('video-date').value = '';
901
+ document.getElementById('video-description').value = '';
902
+ document.getElementById('selected-tags').innerHTML = '';
903
+ document.getElementById('modal-upload-area').innerHTML = `
904
+ <i class="fas fa-cloud-upload-alt text-4xl text-gray-400 mb-3"></i>
905
+ <p class="font-medium">Drag & drop your video here</p>
906
+ <p class="text-sm text-gray-500 dark:text-gray-400 mt-1">or click to browse files</p>
907
+ `;
908
+ selectedFile = null;
909
+ }
910
+
911
+ // Add tag to form
912
+ function addTagToForm(tagText) {
913
+ const tagElement = document.createElement('span');
914
+ tagElement.className = 'tag px-3 py-1 bg-gray-100 dark:bg-gray-700 rounded-full text-xs flex items-center';
915
+ tagElement.innerHTML = `
916
+ <span>${tagText}</span>
917
+ <button class="ml-2 text-gray-500 hover:text-gray-700 dark:hover:text-gray-300">
918
+ <i class="fas fa-times text-xs"></i>
919
+ </button>
920
+ `;
921
+
922
+ const removeBtn = tagElement.querySelector('button');
923
+ removeBtn.addEventListener('click', () => {
924
+ tagElement.remove();
925
+ });
926
+
927
+ document.getElementById('selected-tags').appendChild(tagElement);
928
+ }
929
+
930
+ // Initialize event listeners
931
+ function initEventListeners() {
932
+ // Theme toggle
933
+ const themeToggle = document.getElementById('theme-toggle');
934
+ themeToggle.addEventListener('click', () => {
935
+ document.documentElement.classList.toggle('dark');
936
+ localStorage.setItem('darkMode', document.documentElement.classList.contains('dark'));
937
+ });
938
+
939
+ // Check for saved theme preference
940
+ if (localStorage.getItem('darkMode') === 'true') {
941
+ document.documentElement.classList.add('dark');
942
+ } else if (localStorage.getItem('darkMode') === 'false') {
943
+ document.documentElement.classList.remove('dark');
944
+ } else if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
945
+ document.documentElement.classList.add('dark');
946
+ }
947
+
948
+ // Video modal functionality
949
+ const videoModal = document.getElementById('video-modal');
950
+ const closeModal = document.getElementById('close-modal');
951
+
952
+ closeModal.addEventListener('click', () => {
953
+ videoModal.classList.add('hidden');
954
+ document.getElementById('modal-video').pause();
955
+ document.body.style.overflow = 'auto';
956
+ });
957
+
958
+ // Close modal when clicking outside
959
+ videoModal.addEventListener('click', (e) => {
960
+ if (e.target === videoModal) {
961
+ videoModal.classList.add('hidden');
962
+ document.getElementById('modal-video').pause();
963
+ document.body.style.overflow = 'auto';
964
+ }
965
+ });
966
+
967
+ // Upload modal functionality
968
+ const uploadModal = document.getElementById('upload-modal');
969
+ const closeUploadModal = document.getElementById('close-upload-modal');
970
+ const cancelUpload = document.getElementById('cancel-upload');
971
+ const uploadBtn = document.getElementById('upload-btn');
972
+ const recordBtn = document.getElementById('record-btn');
973
+ const uploadArea = document.getElementById('upload-area');
974
+ const fileInput = document.getElementById('file-input');
975
+ const modalFileInput = document.getElementById('modal-file-input');
976
+ const modalUploadArea = document.getElementById('modal-upload-area');
977
+ const selectedTags = document.getElementById('selected-tags');
978
+ const tagInput = document.getElementById('tag-input');
979
+ const addTagBtn = document.getElementById('add-tag-btn');
980
+ const saveVideoBtn = document.getElementById('save-video');
981
+
982
+ // Open upload modal
983
+ uploadBtn.addEventListener('click', () => {
984
+ uploadModal.classList.remove('hidden');
985
+ document.body.style.overflow = 'hidden';
986
+ });
987
+
988
+ // Open upload modal from record button
989
+ recordBtn.addEventListener('click', () => {
990
+ // In a real app, this would open camera recording
991
+ uploadModal.classList.remove('hidden');
992
+ document.body.style.overflow = 'hidden';
993
+ });
994
+
995
+ // Close upload modal
996
+ closeUploadModal.addEventListener('click', () => {
997
+ uploadModal.classList.add('hidden');
998
+ document.body.style.overflow = 'auto';
999
+ resetUploadForm();
1000
+ });
1001
+
1002
+ cancelUpload.addEventListener('click', () => {
1003
+ uploadModal.classList.add('hidden');
1004
+ document.body.style.overflow = 'auto';
1005
+ resetUploadForm();
1006
+ });
1007
+
1008
+ // Close modal when clicking outside
1009
+ uploadModal.addEventListener('click', (e) => {
1010
+ if (e.target === uploadModal) {
1011
+ uploadModal.classList.add('hidden');
1012
+ document.body.style.overflow = 'auto';
1013
+ resetUploadForm();
1014
+ }
1015
+ });
1016
+
1017
+ // File upload handling
1018
+ uploadArea.addEventListener('click', () => {
1019
+ fileInput.click();
1020
+ });
1021
+
1022
+ modalUploadArea.addEventListener('click', () => {
1023
+ modalFileInput.click();
1024
+ });
1025
+
1026
+ modalFileInput.addEventListener('change', (e) => {
1027
+ if (e.target.files.length > 0) {
1028
+ selectedFile = e.target.files[0];
1029
+ modalUploadArea.innerHTML = `
1030
+ <i class="fas fa-check-circle text-4xl text-green-500 mb-3"></i>
1031
+ <p class="font-medium">${selectedFile.name}</p>
1032
+ <p class="text-sm text-gray-500 dark:text-gray-400 mt-1">Click to change file</p>
1033
+ `;
1034
+ }
1035
+ });
1036
+
1037
+ // Drag and drop functionality
1038
+ ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
1039
+ [uploadArea, modalUploadArea].forEach(area => {
1040
+ area.addEventListener(eventName, preventDefaults, false);
1041
+ });
1042
+ });
1043
+
1044
+ function preventDefaults(e) {
1045
+ e.preventDefault();
1046
+ e.stopPropagation();
1047
+ }
1048
+
1049
+ ['dragenter', 'dragover'].forEach(eventName => {
1050
+ [uploadArea, modalUploadArea].forEach(area => {
1051
+ area.addEventListener(eventName, highlight, false);
1052
+ });
1053
+ });
1054
+
1055
+ ['dragleave', 'drop'].forEach(eventName => {
1056
+ [uploadArea, modalUploadArea].forEach(area => {
1057
+ area.addEventListener(eventName, unhighlight, false);
1058
+ });
1059
+ });
1060
+
1061
+ function highlight() {
1062
+ this.classList.add('dragover');
1063
+ }
1064
+
1065
+ function unhighlight() {
1066
+ this.classList.remove('dragover');
1067
+ }
1068
+
1069
+ uploadArea.addEventListener('drop', handleDrop, false);
1070
+ modalUploadArea.addEventListener('drop', handleModalDrop, false);
1071
+
1072
+ function handleDrop(e) {
1073
+ const dt = e.dataTransfer;
1074
+ const files = dt.files;
1075
+ fileInput.files = files;
1076
+
1077
+ if (files.length > 0) {
1078
+ const fileName = files[0].name;
1079
+ uploadArea.innerHTML = `
1080
+ <i class="fas fa-check-circle text-4xl text-green-500 mb-3"></i>
1081
+ <p class="font-medium">${fileName}</p>
1082
+ <p class="text-sm text-gray-500 dark:text-gray-400 mt-1">Click to change file</p>
1083
+ `;
1084
+ }
1085
+ }
1086
+
1087
+ function handleModalDrop(e) {
1088
+ const dt = e.dataTransfer;
1089
+ const files = dt.files;
1090
+ modalFileInput.files = files;
1091
+
1092
+ if (files.length > 0) {
1093
+ selectedFile = files[0];
1094
+ modalUploadArea.innerHTML = `
1095
+ <i class="fas fa-check-circle text-4xl text-green-500 mb-3"></i>
1096
+ <p class="font-medium">${selectedFile.name}</p>
1097
+ <p class="text-sm text-gray-500 dark:text-gray-400 mt-1">Click to change file</p>
1098
+ `;
1099
+ }
1100
+ }
1101
+
1102
+ // Tag functionality
1103
+ addTagBtn.addEventListener('click', () => {
1104
+ const tagText = tagInput.value.trim();
1105
+ if (tagText) {
1106
+ addTagToForm(tagText);
1107
+ tagInput.value = '';
1108
+ }
1109
+ });
1110
+
1111
+ tagInput.addEventListener('keypress', (e) => {
1112
+ if (e.key === 'Enter') {
1113
+ const tagText = tagInput.value.trim();
1114
+ if (tagText) {
1115
+ addTagToForm(tagText);
1116
+ tagInput.value = '';
1117
+ }
1118
+ }
1119
+ });
1120
+
1121
+ // Add click handler for suggested tags
1122
+ document.querySelectorAll('#upload-modal .tag').forEach(tag => {
1123
+ tag.addEventListener('click', () => {
1124
+ const tagText = tag.textContent;
1125
+ addTagToForm(tagText);
1126
+ });
1127
+ });
1128
+
1129
+ // Save video
1130
+ saveVideoBtn.addEventListener('click', () => {
1131
+ if (!selectedFile) {
1132
+ alert('Please select a video file to upload.');
1133
+ return;
1134
+ }
1135
+
1136
+ const title = document.getElementById('video-title').value.trim();
1137
+ if (!title) {
1138
+ alert('Please enter a title for your video.');
1139
+ return;
1140
+ }
1141
+
1142
+ const workoutType = document.getElementById('workout-name').value.trim();
1143
+ const description = document.getElementById('video-description').value.trim();
1144
+
1145
+ // Get selected tags
1146
+ const tags = [];
1147
+ document.querySelectorAll('#selected-tags span span:first-child').forEach(tag => {
1148
+ tags.push(tag.textContent);
1149
+ });
1150
+
1151
+ // Create metadata object
1152
+ const metadata = {
1153
+ title,
1154
+ workoutType,
1155
+ description,
1156
+ tags
1157
+ };
1158
+
1159
+ // Upload the video
1160
+ uploadVideo(selectedFile, metadata);
1161
+ });
1162
+
1163
+ // Dropdown menus
1164
+ document.querySelectorAll('.dropdown button').forEach(button => {
1165
+ button.addEventListener('click', (e) => {
1166
+ e.stopPropagation();
1167
+ const menu = button.nextElementSibling;
1168
+ menu.classList.toggle('hidden');
1169
+ });
1170
+ });
1171
+
1172
+ // Close dropdown when clicking outside
1173
+ document.addEventListener('click', () => {
1174
+ document.querySelectorAll('.dropdown-menu').forEach(menu => {
1175
+ menu.classList.add('hidden');
1176
+ });
1177
+ });
1178
+
1179
+ // Filter and search functionality
1180
+ sortSelect.addEventListener('change', renderVideos);
1181
+ workoutFilter.addEventListener('change', renderVideos);
1182
+ searchInput.addEventListener('input', renderVideos);
1183
+ }
1184
+
1185
+ // Initialize the app
1186
+ function init() {
1187
+ initEventListeners();
1188
+ loadVideos();
1189
+ }
1190
+
1191
+ // Initialize when DOM is loaded
1192
+ document.addEventListener('DOMContentLoaded', init);
1193
+ </script>
1194
+ <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=frucht/workout-archiver" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
1195
+ </html>
prompts.txt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ can you connect it to database or maybe even google drive
2
+ itaybarak01@gmail.com You can't sign in to this app because it doesn't comply with Google's OAuth 2.0 policy for keeping apps secure. You can let the app developer know that this app doesn't comply with one or more Google validation rules. Learn more about this error If you are a developer of this app, see error details. Error 400: invalid_request
3
+ cess blocked: Authorization Error itaybarak01@gmail.com You can't sign in to this app because it doesn't comply with Google's OAuth 2.0 policy for keeping apps secure. You can let the app developer know that this app doesn't comply with one or more Google validation rules. Learn more about this error If you are a developer of this app, see error details. Error 400: invalid_request