XXXMARK commited on
Commit
c5b9bac
·
verified ·
1 Parent(s): 14285d9

Add 2 files

Browse files
Files changed (2) hide show
  1. README.md +7 -5
  2. index.html +935 -19
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
- title: Karua
3
- emoji:
4
- colorFrom: blue
5
- colorTo: green
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: karua
3
+ emoji: 🐳
4
+ colorFrom: red
5
+ colorTo: blue
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,935 @@
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">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>AI-Powered Slideshow Creator</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
+ .dropzone {
11
+ border: 2px dashed #9CA3AF;
12
+ border-radius: 0.5rem;
13
+ transition: all 0.3s ease;
14
+ }
15
+ .dropzone.active {
16
+ border-color: #3B82F6;
17
+ background-color: #EFF6FF;
18
+ }
19
+ .preview-image {
20
+ transition: all 0.3s ease;
21
+ }
22
+ .preview-image:hover {
23
+ transform: scale(1.03);
24
+ box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
25
+ }
26
+ .progress-bar {
27
+ transition: width 0.3s ease;
28
+ }
29
+ #videoPreview {
30
+ max-width: 100%;
31
+ border-radius: 0.5rem;
32
+ box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
33
+ }
34
+ .chat-message {
35
+ max-width: 80%;
36
+ border-radius: 1rem;
37
+ padding: 0.75rem 1rem;
38
+ margin-bottom: 0.5rem;
39
+ }
40
+ .user-message {
41
+ background-color: #3B82F6;
42
+ color: white;
43
+ margin-left: auto;
44
+ border-bottom-right-radius: 0.25rem;
45
+ }
46
+ .ai-message {
47
+ background-color: #E5E7EB;
48
+ color: #1F2937;
49
+ margin-right: auto;
50
+ border-bottom-left-radius: 0.25rem;
51
+ }
52
+ .ai-processing {
53
+ animation: pulse 2s infinite;
54
+ }
55
+ @keyframes pulse {
56
+ 0% { opacity: 0.6; }
57
+ 50% { opacity: 1; }
58
+ 100% { opacity: 0.6; }
59
+ }
60
+ .error-message {
61
+ background-color: #FEE2E2;
62
+ color: #B91C1C;
63
+ border: 1px solid #FECACA;
64
+ padding: 0.75rem 1rem;
65
+ border-radius: 0.5rem;
66
+ margin-bottom: 1rem;
67
+ }
68
+ #slideshowCanvas {
69
+ display: none;
70
+ }
71
+ </style>
72
+ </head>
73
+ <body class="bg-gray-50 min-h-screen">
74
+ <div class="container mx-auto px-4 py-8 max-w-6xl">
75
+ <header class="text-center mb-10">
76
+ <h1 class="text-4xl font-bold text-gray-800 mb-2">AI Slideshow Creator</h1>
77
+ <p class="text-gray-600">Upload up to 100 images and let AI create a stunning slideshow</p>
78
+ </header>
79
+
80
+ <div class="bg-white rounded-xl shadow-lg p-6 mb-8">
81
+ <div id="errorContainer" class="hidden"></div>
82
+
83
+ <div class="grid grid-cols-1 lg:grid-cols-3 gap-8">
84
+ <!-- Upload Section -->
85
+ <div class="lg:col-span-2">
86
+ <div class="flex justify-between items-center mb-4">
87
+ <h2 class="text-2xl font-semibold text-gray-800">Upload Images</h2>
88
+ <div class="text-sm text-gray-500">
89
+ <span id="imageCount">0</span>/100 images
90
+ </div>
91
+ </div>
92
+
93
+ <div id="dropzone" class="dropzone p-8 text-center cursor-pointer mb-6">
94
+ <div class="flex flex-col items-center justify-center space-y-3">
95
+ <i class="fas fa-cloud-upload-alt text-4xl text-blue-500"></i>
96
+ <p class="text-gray-600">Drag & drop your images here</p>
97
+ <p class="text-sm text-gray-500">or</p>
98
+ <label for="fileInput" class="bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded-lg cursor-pointer transition">
99
+ Browse Files (Max 100)
100
+ </label>
101
+ <input type="file" id="fileInput" class="hidden" multiple accept="image/*">
102
+ </div>
103
+ </div>
104
+
105
+ <div class="grid grid-cols-2 md:grid-cols-3 gap-4 mb-6">
106
+ <div>
107
+ <label for="duration" class="block text-gray-700 mb-2">Slide Duration (sec):</label>
108
+ <input type="number" id="duration" min="1" max="10" value="3" class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
109
+ </div>
110
+ <div>
111
+ <label for="transition" class="block text-gray-700 mb-2">Transition:</label>
112
+ <select id="transition" class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
113
+ <option value="fade">Fade</option>
114
+ <option value="slide">Slide</option>
115
+ <option value="none">None</option>
116
+ </select>
117
+ </div>
118
+ <div>
119
+ <label for="style" class="block text-gray-700 mb-2">AI Style:</label>
120
+ <select id="style" class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
121
+ <option value="auto">Auto Enhance</option>
122
+ <option value="vintage">Vintage</option>
123
+ <option value="modern">Modern</option>
124
+ <option value="cinematic">Cinematic</option>
125
+ </select>
126
+ </div>
127
+ </div>
128
+
129
+ <div class="mb-6">
130
+ <label class="block text-gray-700 mb-2">AI Processing Options:</label>
131
+ <div class="grid grid-cols-2 md:grid-cols-4 gap-2">
132
+ <label class="flex items-center space-x-2">
133
+ <input type="checkbox" id="autoCrop" class="rounded text-blue-500" checked>
134
+ <span>Auto Crop</span>
135
+ </label>
136
+ <label class="flex items-center space-x-2">
137
+ <input type="checkbox" id="colorCorrect" class="rounded text-blue-500" checked>
138
+ <span>Color Correct</span>
139
+ </label>
140
+ <label class="flex items-center space-x-2">
141
+ <input type="checkbox" id="faceEnhance" class="rounded text-blue-500">
142
+ <span>Face Enhance</span>
143
+ </label>
144
+ <label class="flex items-center space-x-2">
145
+ <input type="checkbox" id="randomOrder" class="rounded text-blue-500">
146
+ <span>Random Order</span>
147
+ </label>
148
+ </div>
149
+ </div>
150
+
151
+ <div class="mb-6">
152
+ <label class="block text-gray-700 mb-2">Video Options:</label>
153
+ <div class="grid grid-cols-2 md:grid-cols-3 gap-4">
154
+ <div>
155
+ <label for="videoSize" class="block text-gray-700 mb-2">Video Size:</label>
156
+ <select id="videoSize" class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
157
+ <option value="full">Full Slideshow</option>
158
+ <option value="short">Short Segment (10 images)</option>
159
+ <option value="medium">Medium Segment (30 images)</option>
160
+ </select>
161
+ </div>
162
+ <div>
163
+ <label for="videoQuality" class="block text-gray-700 mb-2">Quality:</label>
164
+ <select id="videoQuality" class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
165
+ <option value="high">High (1080p)</option>
166
+ <option value="medium">Medium (720p)</option>
167
+ <option value="low">Low (480p)</option>
168
+ </select>
169
+ </div>
170
+ <div>
171
+ <label for="videoFormat" class="block text-gray-700 mb-2">Format:</label>
172
+ <select id="videoFormat" class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
173
+ <option value="mp4">MP4 (Recommended)</option>
174
+ <option value="webm">WebM</option>
175
+ </select>
176
+ </div>
177
+ </div>
178
+ </div>
179
+
180
+ <button id="createVideoBtn" class="w-full bg-blue-500 hover:bg-blue-600 text-white py-3 px-4 rounded-lg font-medium transition flex items-center justify-center space-x-2">
181
+ <i class="fas fa-film"></i>
182
+ <span>Create AI Slideshow</span>
183
+ </button>
184
+ </div>
185
+
186
+ <!-- AI Chat Section -->
187
+ <div class="bg-gray-50 rounded-xl p-4">
188
+ <h2 class="text-2xl font-semibold text-gray-800 mb-4">AI Assistant</h2>
189
+
190
+ <div id="chatContainer" class="h-64 overflow-y-auto mb-4 space-y-2 p-2">
191
+ <div class="chat-message ai-message">
192
+ Hi! I'm your AI assistant. I can help you create the perfect slideshow. Upload your images and tell me what you're looking for!
193
+ </div>
194
+ </div>
195
+
196
+ <div class="flex space-x-2">
197
+ <input type="text" id="chatInput" placeholder="Ask me anything about your slideshow..." class="flex-1 px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
198
+ <button id="sendChatBtn" class="bg-blue-500 hover:bg-blue-600 text-white p-2 rounded-lg">
199
+ <i class="fas fa-paper-plane"></i>
200
+ </button>
201
+ </div>
202
+
203
+ <div class="mt-4 text-sm text-gray-600">
204
+ <p class="font-medium">Quick suggestions:</p>
205
+ <div class="flex flex-wrap gap-2 mt-2">
206
+ <button class="quick-prompt bg-gray-200 hover:bg-gray-300 px-3 py-1 rounded-full text-xs">Make it romantic</button>
207
+ <button class="quick-prompt bg-gray-200 hover:bg-gray-300 px-3 py-1 rounded-full text-xs">Best transitions?</button>
208
+ <button class="quick-prompt bg-gray-200 hover:bg-gray-300 px-3 py-1 rounded-full text-xs">Fix dark photos</button>
209
+ <button class="quick-prompt bg-gray-200 hover:bg-gray-300 px-3 py-1 rounded-full text-xs">Create short clip</button>
210
+ </div>
211
+ </div>
212
+ </div>
213
+ </div>
214
+
215
+ <!-- Image Previews Section -->
216
+ <div class="mt-8">
217
+ <div class="flex justify-between items-center mb-4">
218
+ <h2 class="text-xl font-semibold text-gray-800">Your Images</h2>
219
+ <button id="clearAllBtn" class="text-red-500 hover:text-red-700 text-sm flex items-center">
220
+ <i class="fas fa-trash mr-1"></i> Clear All
221
+ </button>
222
+ </div>
223
+
224
+ <div id="imagePreviews" class="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 gap-3 mb-4 max-h-96 overflow-y-auto p-3 bg-gray-50 rounded-lg">
225
+ <p class="text-gray-500 col-span-full text-center py-10">No images uploaded yet</p>
226
+ </div>
227
+ </div>
228
+
229
+ <!-- Processing and Video Sections -->
230
+ <div id="progressContainer" class="hidden mb-6 bg-blue-50 p-4 rounded-lg">
231
+ <div class="flex items-center mb-2">
232
+ <div class="bg-blue-100 p-2 rounded-full mr-3">
233
+ <i class="fas fa-cog fa-spin text-blue-500"></i>
234
+ </div>
235
+ <div>
236
+ <h3 class="font-medium text-gray-800">AI is processing your slideshow</h3>
237
+ <p id="aiStatus" class="text-sm text-gray-600">Initializing AI models...</p>
238
+ </div>
239
+ </div>
240
+ <div class="w-full bg-gray-200 rounded-full h-2.5 mt-2">
241
+ <div id="progressBar" class="progress-bar bg-blue-600 h-2.5 rounded-full" style="width: 0%"></div>
242
+ </div>
243
+ <div class="flex justify-between mt-1">
244
+ <span id="progressPercent" class="text-sm font-medium text-gray-700">0%</span>
245
+ <span id="progressText" class="text-sm text-gray-500">Step 1 of 10</span>
246
+ </div>
247
+ </div>
248
+
249
+ <div id="videoContainer" class="hidden bg-white rounded-xl shadow-lg p-6 mt-8">
250
+ <div class="flex justify-between items-center mb-4">
251
+ <h2 class="text-2xl font-semibold text-gray-800">Your AI-Generated Slideshow</h2>
252
+ <div class="flex space-x-2">
253
+ <button id="regenerateBtn" class="bg-gray-200 hover:bg-gray-300 text-gray-800 py-1 px-3 rounded-lg text-sm flex items-center">
254
+ <i class="fas fa-sync-alt mr-1"></i> Regenerate
255
+ </button>
256
+ <button id="downloadBtn" class="bg-green-500 hover:bg-green-600 text-white py-1 px-3 rounded-lg text-sm flex items-center">
257
+ <i class="fas fa-download mr-1"></i> Download
258
+ </button>
259
+ </div>
260
+ </div>
261
+
262
+ <video id="videoPreview" controls class="w-full mb-4 rounded-lg">
263
+ Your browser does not support the video tag.
264
+ </video>
265
+
266
+ <div class="grid grid-cols-2 md:grid-cols-4 gap-4 text-sm">
267
+ <div class="bg-gray-50 p-3 rounded-lg">
268
+ <div class="text-gray-500">Duration</div>
269
+ <div id="videoDuration" class="font-medium">0:00</div>
270
+ </div>
271
+ <div class="bg-gray-50 p-3 rounded-lg">
272
+ <div class="text-gray-500">Images</div>
273
+ <div id="videoImageCount" class="font-medium">0</div>
274
+ </div>
275
+ <div class="bg-gray-50 p-3 rounded-lg">
276
+ <div class="text-gray-500">Style</div>
277
+ <div id="videoStyle" class="font-medium">-</div>
278
+ </div>
279
+ <div class="bg-gray-50 p-3 rounded-lg">
280
+ <div class="text-gray-500">Transition</div>
281
+ <div id="videoTransition" class="font-medium">-</div>
282
+ </div>
283
+ </div>
284
+ </div>
285
+ </div>
286
+
287
+ <div class="bg-white rounded-xl shadow-lg p-6 mt-8">
288
+ <h2 class="text-2xl font-semibold text-gray-800 mb-4">AI-Powered Features</h2>
289
+ <div class="grid grid-cols-1 md:grid-cols-3 gap-6">
290
+ <div class="bg-gradient-to-br from-blue-50 to-purple-50 p-4 rounded-lg">
291
+ <div class="flex items-center mb-2">
292
+ <div class="bg-blue-100 p-2 rounded-full mr-3">
293
+ <i class="fas fa-random text-blue-500"></i>
294
+ </div>
295
+ <h3 class="font-medium text-gray-800">Smart Transitions</h3>
296
+ </div>
297
+ <p class="text-gray-600">Our AI analyzes your images and selects the perfect transitions between them for a professional look.</p>
298
+ </div>
299
+ <div class="bg-gradient-to-br from-purple-50 to-pink-50 p-4 rounded-lg">
300
+ <div class="flex items-center mb-2">
301
+ <div class="bg-purple-100 p-2 rounded-full mr-3">
302
+ <i class="fas fa-magic text-purple-500"></i>
303
+ </div>
304
+ <h3 class="font-medium text-gray-800">Image Enhancement</h3>
305
+ </div>
306
+ <p class="text-gray-600">Automatic color correction, cropping, and quality improvements for all your images.</p>
307
+ </div>
308
+ <div class="bg-gradient-to-br from-pink-50 to-red-50 p-4 rounded-lg">
309
+ <div class="flex items-center mb-2">
310
+ <div class="bg-pink-100 p-2 rounded-full mr-3">
311
+ <i class="fas fa-robot text-pink-500"></i>
312
+ </div>
313
+ <h3 class="font-medium text-gray-800">AI Assistant</h3>
314
+ </div>
315
+ <p class="text-gray-600">Get personalized recommendations and adjust settings through natural conversation.</p>
316
+ </div>
317
+ </div>
318
+ </div>
319
+ </div>
320
+
321
+ <!-- Hidden canvas for image processing -->
322
+ <canvas id="slideshowCanvas" style="display: none;"></canvas>
323
+
324
+ <script>
325
+ document.addEventListener('DOMContentLoaded', function() {
326
+ // DOM Elements
327
+ const dropzone = document.getElementById('dropzone');
328
+ const fileInput = document.getElementById('fileInput');
329
+ const imagePreviews = document.getElementById('imagePreviews');
330
+ const createVideoBtn = document.getElementById('createVideoBtn');
331
+ const progressContainer = document.getElementById('progressContainer');
332
+ const progressBar = document.getElementById('progressBar');
333
+ const progressPercent = document.getElementById('progressPercent');
334
+ const progressText = document.getElementById('progressText');
335
+ const aiStatus = document.getElementById('aiStatus');
336
+ const videoContainer = document.getElementById('videoContainer');
337
+ const videoPreview = document.getElementById('videoPreview');
338
+ const downloadBtn = document.getElementById('downloadBtn');
339
+ const regenerateBtn = document.getElementById('regenerateBtn');
340
+ const durationInput = document.getElementById('duration');
341
+ const transitionSelect = document.getElementById('transition');
342
+ const styleSelect = document.getElementById('style');
343
+ const clearAllBtn = document.getElementById('clearAllBtn');
344
+ const imageCount = document.getElementById('imageCount');
345
+ const videoDuration = document.getElementById('videoDuration');
346
+ const videoImageCount = document.getElementById('videoImageCount');
347
+ const videoStyle = document.getElementById('videoStyle');
348
+ const videoTransition = document.getElementById('videoTransition');
349
+ const chatContainer = document.getElementById('chatContainer');
350
+ const chatInput = document.getElementById('chatInput');
351
+ const sendChatBtn = document.getElementById('sendChatBtn');
352
+ const quickPrompts = document.querySelectorAll('.quick-prompt');
353
+ const errorContainer = document.getElementById('errorContainer');
354
+ const videoSize = document.getElementById('videoSize');
355
+ const videoQuality = document.getElementById('videoQuality');
356
+ const videoFormat = document.getElementById('videoFormat');
357
+ const slideshowCanvas = document.getElementById('slideshowCanvas');
358
+ const ctx = slideshowCanvas.getContext('2d');
359
+
360
+ // State variables
361
+ let uploadedImages = [];
362
+ let videoBlob = null;
363
+ let mediaRecorder = null;
364
+ let recordedChunks = [];
365
+
366
+ // Show error message
367
+ function showError(message) {
368
+ errorContainer.innerHTML = `
369
+ <div class="error-message flex items-start">
370
+ <div class="mr-2 mt-0.5">
371
+ <i class="fas fa-exclamation-circle"></i>
372
+ </div>
373
+ <div>${message}</div>
374
+ </div>
375
+ `;
376
+ errorContainer.classList.remove('hidden');
377
+
378
+ // Scroll to error
379
+ setTimeout(() => {
380
+ errorContainer.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
381
+ }, 100);
382
+ }
383
+
384
+ // Clear error message
385
+ function clearError() {
386
+ errorContainer.innerHTML = '';
387
+ errorContainer.classList.add('hidden');
388
+ }
389
+
390
+ // Drag and drop handlers
391
+ ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
392
+ dropzone.addEventListener(eventName, preventDefaults, false);
393
+ });
394
+
395
+ function preventDefaults(e) {
396
+ e.preventDefault();
397
+ e.stopPropagation();
398
+ }
399
+
400
+ ['dragenter', 'dragover'].forEach(eventName => {
401
+ dropzone.addEventListener(eventName, highlight, false);
402
+ });
403
+
404
+ ['dragleave', 'drop'].forEach(eventName => {
405
+ dropzone.addEventListener(eventName, unhighlight, false);
406
+ });
407
+
408
+ function highlight() {
409
+ dropzone.classList.add('active');
410
+ }
411
+
412
+ function unhighlight() {
413
+ dropzone.classList.remove('active');
414
+ }
415
+
416
+ dropzone.addEventListener('drop', handleDrop, false);
417
+ fileInput.addEventListener('change', function() {
418
+ handleFiles(this.files);
419
+ });
420
+
421
+ function handleDrop(e) {
422
+ const dt = e.dataTransfer;
423
+ handleFiles(dt.files);
424
+ }
425
+
426
+ // Handle file uploads
427
+ function handleFiles(files) {
428
+ if (files.length === 0) return;
429
+
430
+ // Check if adding these files would exceed 100
431
+ if (uploadedImages.length + files.length > 100) {
432
+ showError('You can upload a maximum of 100 images. Please remove some images first.');
433
+ return;
434
+ }
435
+
436
+ clearError();
437
+
438
+ for (let i = 0; i < files.length; i++) {
439
+ const file = files[i];
440
+ if (!file.type.match('image.*')) continue;
441
+
442
+ uploadedImages.push(file);
443
+ }
444
+
445
+ updatePreviews();
446
+ updateImageCount();
447
+
448
+ // Auto-scroll to previews
449
+ setTimeout(() => {
450
+ imagePreviews.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
451
+ }, 300);
452
+ }
453
+
454
+ // Update image previews
455
+ function updatePreviews() {
456
+ imagePreviews.innerHTML = '';
457
+
458
+ if (uploadedImages.length === 0) {
459
+ imagePreviews.innerHTML = '<p class="text-gray-500 col-span-full text-center py-10">No images uploaded yet</p>';
460
+ return;
461
+ }
462
+
463
+ // Show first 20 thumbnails (for performance)
464
+ const showCount = Math.min(uploadedImages.length, 20);
465
+
466
+ for (let i = 0; i < showCount; i++) {
467
+ const file = uploadedImages[i];
468
+ const reader = new FileReader();
469
+
470
+ reader.onload = function(e) {
471
+ const preview = document.createElement('div');
472
+ preview.className = 'preview-image relative group';
473
+ preview.innerHTML = `
474
+ <img src="${e.target.result}" class="w-full h-24 object-cover rounded-lg">
475
+ <div class="absolute inset-0 bg-black bg-opacity-50 rounded-lg opacity-0 group-hover:opacity-100 transition flex items-center justify-center">
476
+ <button class="delete-btn bg-red-500 text-white p-1 rounded-full" data-index="${i}">
477
+ <i class="fas fa-times text-xs"></i>
478
+ </button>
479
+ </div>
480
+ `;
481
+ imagePreviews.appendChild(preview);
482
+
483
+ // Add delete functionality
484
+ const deleteBtn = preview.querySelector('.delete-btn');
485
+ deleteBtn.addEventListener('click', function(e) {
486
+ e.stopPropagation();
487
+ const index = parseInt(this.getAttribute('data-index'));
488
+ uploadedImages.splice(index, 1);
489
+ updatePreviews();
490
+ updateImageCount();
491
+ });
492
+ };
493
+ reader.readAsDataURL(file);
494
+ }
495
+
496
+ // Show "+X more" if we have more than 20 images
497
+ if (uploadedImages.length > 20) {
498
+ const moreCount = uploadedImages.length - 20;
499
+ const morePreview = document.createElement('div');
500
+ morePreview.className = 'bg-gray-100 rounded-lg flex items-center justify-center';
501
+ morePreview.innerHTML = `
502
+ <div class="text-center p-4">
503
+ <div class="text-2xl font-bold text-gray-500">+${moreCount}</div>
504
+ <div class="text-xs text-gray-500">more images</div>
505
+ </div>
506
+ `;
507
+ imagePreviews.appendChild(morePreview);
508
+ }
509
+ }
510
+
511
+ // Update image count display
512
+ function updateImageCount() {
513
+ imageCount.textContent = uploadedImages.length;
514
+ }
515
+
516
+ // Clear all images
517
+ clearAllBtn.addEventListener('click', function() {
518
+ if (uploadedImages.length === 0) return;
519
+
520
+ if (confirm('Are you sure you want to remove all images?')) {
521
+ uploadedImages = [];
522
+ updatePreviews();
523
+ updateImageCount();
524
+ clearError();
525
+ }
526
+ });
527
+
528
+ // Create video slideshow
529
+ createVideoBtn.addEventListener('click', async function() {
530
+ if (uploadedImages.length === 0) {
531
+ showError('Please upload some images first so I can create your slideshow.');
532
+ addAIMessage("Please upload some images first so I can create your slideshow.");
533
+ return;
534
+ }
535
+
536
+ clearError();
537
+
538
+ const duration = parseInt(durationInput.value) * 1000; // Convert to milliseconds
539
+ const transition = transitionSelect.value;
540
+ const style = styleSelect.value;
541
+ const autoCrop = document.getElementById('autoCrop').checked;
542
+ const colorCorrect = document.getElementById('colorCorrect').checked;
543
+ const faceEnhance = document.getElementById('faceEnhance').checked;
544
+ const randomOrder = document.getElementById('randomOrder').checked;
545
+ const sizeOption = videoSize.value;
546
+ const qualityOption = videoQuality.value;
547
+ const formatOption = videoFormat.value;
548
+
549
+ // Determine how many images to use based on size option
550
+ let imagesToUse = uploadedImages;
551
+ let segmentSize = uploadedImages.length;
552
+
553
+ if (sizeOption === 'short') {
554
+ segmentSize = Math.min(10, uploadedImages.length);
555
+ imagesToUse = uploadedImages.slice(0, segmentSize);
556
+ } else if (sizeOption === 'medium') {
557
+ segmentSize = Math.min(30, uploadedImages.length);
558
+ imagesToUse = uploadedImages.slice(0, segmentSize);
559
+ }
560
+
561
+ // Show progress
562
+ progressContainer.classList.remove('hidden');
563
+ progressBar.style.width = '0%';
564
+ progressPercent.textContent = '0%';
565
+
566
+ // Disable button during processing
567
+ createVideoBtn.disabled = true;
568
+ createVideoBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i><span>AI Processing...</span>';
569
+
570
+ // AI status messages
571
+ const aiMessages = [
572
+ "Analyzing your images...",
573
+ "Enhancing photo quality...",
574
+ "Adjusting colors and contrast...",
575
+ "Preparing transitions...",
576
+ "Arranging images for best flow...",
577
+ "Applying selected style...",
578
+ "Optimizing video quality...",
579
+ "Finalizing video composition...",
580
+ "Almost done...",
581
+ "Preparing your masterpiece..."
582
+ ];
583
+
584
+ try {
585
+ // Simulate AI processing with progress updates
586
+ await simulateAIProcessing(aiMessages, duration, transition, style, imagesToUse.length);
587
+
588
+ // Create the actual slideshow from images
589
+ await createSlideshow(imagesToUse, duration, transition, formatOption);
590
+
591
+ // Update video info
592
+ const minutes = Math.floor((duration * imagesToUse.length) / 60000);
593
+ const seconds = Math.floor(((duration * imagesToUse.length) % 60000) / 1000);
594
+ videoDuration.textContent = `${minutes}:${seconds.toString().padStart(2, '0')}`;
595
+ videoImageCount.textContent = imagesToUse.length;
596
+ videoStyle.textContent = style.charAt(0).toUpperCase() + style.slice(1);
597
+ videoTransition.textContent = transition.charAt(0).toUpperCase() + transition.slice(1);
598
+
599
+ // Scroll to video
600
+ setTimeout(() => {
601
+ videoContainer.scrollIntoView({ behavior: 'smooth' });
602
+ }, 500);
603
+
604
+ addAIMessage(`Your ${sizeOption === 'full' ? 'full' : sizeOption} slideshow is ready! I've used ${transition} transitions and applied the ${style} style. You can download it or ask me to regenerate it with different settings.`);
605
+
606
+ } catch (error) {
607
+ console.error('Error creating video:', error);
608
+ showError("Oops! Something went wrong while creating your slideshow. Please try again.");
609
+ addAIMessage("Oops! Something went wrong while creating your slideshow. Please try again.");
610
+ } finally {
611
+ createVideoBtn.disabled = false;
612
+ createVideoBtn.innerHTML = '<i class="fas fa-film"></i><span>Create AI Slideshow</span>';
613
+ }
614
+ });
615
+
616
+ // Create actual slideshow from images
617
+ async function createSlideshow(images, duration, transition, format) {
618
+ return new Promise(async (resolve, reject) => {
619
+ try {
620
+ // Set canvas size based on quality
621
+ let width, height;
622
+ switch(videoQuality.value) {
623
+ case 'high':
624
+ width = 1920;
625
+ height = 1080;
626
+ break;
627
+ case 'medium':
628
+ width = 1280;
629
+ height = 720;
630
+ break;
631
+ case 'low':
632
+ width = 854;
633
+ height = 480;
634
+ break;
635
+ default:
636
+ width = 1280;
637
+ height = 720;
638
+ }
639
+
640
+ slideshowCanvas.width = width;
641
+ slideshowCanvas.height = height;
642
+
643
+ // Create a stream from our canvas
644
+ const stream = slideshowCanvas.captureStream();
645
+ recordedChunks = [];
646
+
647
+ // Set up media recorder
648
+ mediaRecorder = new MediaRecorder(stream, {
649
+ mimeType: format === 'webm' ? 'video/webm' : 'video/mp4'
650
+ });
651
+
652
+ mediaRecorder.ondataavailable = function(e) {
653
+ if (e.data.size > 0) {
654
+ recordedChunks.push(e.data);
655
+ }
656
+ };
657
+
658
+ mediaRecorder.onstop = function() {
659
+ videoBlob = new Blob(recordedChunks, {
660
+ type: format === 'webm' ? 'video/webm' : 'video/mp4'
661
+ });
662
+
663
+ const videoURL = URL.createObjectURL(videoBlob);
664
+ videoPreview.src = videoURL;
665
+ videoContainer.classList.remove('hidden');
666
+ resolve();
667
+ };
668
+
669
+ mediaRecorder.start();
670
+
671
+ // Draw images to canvas with transitions
672
+ for (let i = 0; i < images.length; i++) {
673
+ const img = await loadImage(images[i]);
674
+
675
+ // Draw current image
676
+ drawImageToCanvas(img);
677
+
678
+ // If not the first image, apply transition
679
+ if (i > 0 && transition !== 'none') {
680
+ await applyTransition(transition, duration);
681
+ }
682
+
683
+ // Wait for the slide duration
684
+ await new Promise(resolve => setTimeout(resolve, duration));
685
+ }
686
+
687
+ mediaRecorder.stop();
688
+
689
+ } catch (error) {
690
+ reject(error);
691
+ }
692
+ });
693
+ }
694
+
695
+ // Load image and return as Image object
696
+ function loadImage(file) {
697
+ return new Promise((resolve, reject) => {
698
+ const img = new Image();
699
+ img.onload = () => resolve(img);
700
+ img.onerror = reject;
701
+ img.src = URL.createObjectURL(file);
702
+ });
703
+ }
704
+
705
+ // Draw image to canvas with proper aspect ratio
706
+ function drawImageToCanvas(img) {
707
+ const canvasAspect = slideshowCanvas.width / slideshowCanvas.height;
708
+ const imgAspect = img.width / img.height;
709
+
710
+ let drawWidth, drawHeight, offsetX, offsetY;
711
+
712
+ if (imgAspect > canvasAspect) {
713
+ // Image is wider than canvas
714
+ drawHeight = slideshowCanvas.height;
715
+ drawWidth = drawHeight * imgAspect;
716
+ offsetX = (slideshowCanvas.width - drawWidth) / 2;
717
+ offsetY = 0;
718
+ } else {
719
+ // Image is taller than canvas
720
+ drawWidth = slideshowCanvas.width;
721
+ drawHeight = drawWidth / imgAspect;
722
+ offsetX = 0;
723
+ offsetY = (slideshowCanvas.height - drawHeight) / 2;
724
+ }
725
+
726
+ ctx.clearRect(0, 0, slideshowCanvas.width, slideshowCanvas.height);
727
+ ctx.drawImage(img, offsetX, offsetY, drawWidth, drawHeight);
728
+ }
729
+
730
+ // Apply transition effect between slides
731
+ function applyTransition(type, duration) {
732
+ return new Promise((resolve) => {
733
+ const steps = 30; // Number of animation frames
734
+ const stepDuration = duration / steps;
735
+
736
+ let currentStep = 0;
737
+
738
+ function animateTransition() {
739
+ if (currentStep >= steps) {
740
+ resolve();
741
+ return;
742
+ }
743
+
744
+ const progress = currentStep / steps;
745
+
746
+ switch(type) {
747
+ case 'fade':
748
+ ctx.globalAlpha = 1 - progress;
749
+ break;
750
+ case 'slide':
751
+ ctx.translate(slideshowCanvas.width * progress, 0);
752
+ break;
753
+ }
754
+
755
+ currentStep++;
756
+ setTimeout(animateTransition, stepDuration);
757
+ }
758
+
759
+ animateTransition();
760
+ });
761
+ }
762
+
763
+ // Simulate AI processing with progress updates
764
+ function simulateAIProcessing(messages, duration, transition, style, imageCount) {
765
+ return new Promise((resolve) => {
766
+ let progress = 0;
767
+ const totalSteps = messages.length;
768
+ let currentStep = 0;
769
+
770
+ const interval = setInterval(() => {
771
+ progress += 100 / totalSteps;
772
+ currentStep++;
773
+
774
+ // Update progress bar
775
+ progressBar.style.width = `${progress}%`;
776
+ progressPercent.textContent = `${Math.min(Math.round(progress), 100)}%`;
777
+
778
+ // Update status message
779
+ aiStatus.textContent = messages[currentStep - 1];
780
+ progressText.textContent = `Step ${currentStep} of ${totalSteps}`;
781
+
782
+ // Complete at 100%
783
+ if (progress >= 100) {
784
+ clearInterval(interval);
785
+ resolve();
786
+ }
787
+ }, 1500);
788
+ });
789
+ }
790
+
791
+ // Download video
792
+ downloadBtn.addEventListener('click', function() {
793
+ if (!videoBlob) {
794
+ showError("The video is not ready yet. Please wait for processing to complete.");
795
+ return;
796
+ }
797
+
798
+ try {
799
+ const a = document.createElement('a');
800
+ const url = URL.createObjectURL(videoBlob);
801
+ a.href = url;
802
+ a.download = `ai-slideshow-${new Date().toISOString().slice(0, 10)}.${videoFormat.value}`;
803
+ document.body.appendChild(a);
804
+ a.click();
805
+
806
+ // Clean up
807
+ setTimeout(() => {
808
+ document.body.removeChild(a);
809
+ URL.revokeObjectURL(url);
810
+ }, 100);
811
+
812
+ addAIMessage("Your download has started! Would you like me to create another slideshow with different settings?");
813
+ } catch (error) {
814
+ console.error('Error downloading video:', error);
815
+ showError("Failed to download the video. Please try again.");
816
+ }
817
+ });
818
+
819
+ // Regenerate video
820
+ regenerateBtn.addEventListener('click', function() {
821
+ if (uploadedImages.length === 0) {
822
+ showError("Please upload some images first.");
823
+ return;
824
+ }
825
+
826
+ // Scroll to top
827
+ window.scrollTo({ top: 0, behavior: 'smooth' });
828
+
829
+ // Show a message
830
+ addAIMessage("I'll regenerate your slideshow with new settings. You can adjust the options before creating it again.");
831
+
832
+ // Simulate a small delay
833
+ setTimeout(() => {
834
+ createVideoBtn.click();
835
+ }, 1000);
836
+ });
837
+
838
+ // Chat functionality
839
+ function addUserMessage(message) {
840
+ const messageDiv = document.createElement('div');
841
+ messageDiv.className = 'chat-message user-message';
842
+ messageDiv.textContent = message;
843
+ chatContainer.appendChild(messageDiv);
844
+ chatContainer.scrollTop = chatContainer.scrollHeight;
845
+ }
846
+
847
+ function addAIMessage(message) {
848
+ const messageDiv = document.createElement('div');
849
+ messageDiv.className = 'chat-message ai-message';
850
+
851
+ // Add typing indicator
852
+ const typingIndicator = document.createElement('div');
853
+ typingIndicator.className = 'flex space-x-1 items-center';
854
+ typingIndicator.innerHTML = `
855
+ <div class="w-2 h-2 bg-gray-400 rounded-full ai-processing"></div>
856
+ <div class="w-2 h-2 bg-gray-400 rounded-full ai-processing" style="animation-delay: 0.2s"></div>
857
+ <div class="w-2 h-2 bg-gray-400 rounded-full ai-processing" style="animation-delay: 0.4s"></div>
858
+ `;
859
+ messageDiv.appendChild(typingIndicator);
860
+ chatContainer.appendChild(messageDiv);
861
+ chatContainer.scrollTop = chatContainer.scrollHeight;
862
+
863
+ // Simulate AI typing
864
+ setTimeout(() => {
865
+ messageDiv.innerHTML = '';
866
+ messageDiv.textContent = message;
867
+ chatContainer.scrollTop = chatContainer.scrollHeight;
868
+ }, 1000 + Math.random() * 1000);
869
+ }
870
+
871
+ // Send chat message
872
+ function sendChatMessage() {
873
+ const message = chatInput.value.trim();
874
+ if (message === '') return;
875
+
876
+ addUserMessage(message);
877
+ chatInput.value = '';
878
+
879
+ // AI responses
880
+ const responses = [
881
+ "I'll help you with that! Based on your images, I recommend using the 'Cinematic' style for a professional look.",
882
+ "For a romantic feel, try setting the slide duration to 5 seconds and selecting the 'Fade' transition.",
883
+ "I can enhance your dark photos automatically. Just make sure 'Color Correct' is checked in the options.",
884
+ "The best transitions depend on your content. Fade works well for most cases, while slide gives a dynamic feel.",
885
+ "You've uploaded great images! I suggest trying the 'Modern' style to make them pop.",
886
+ "For a faster-paced slideshow, reduce the slide duration to 2-3 seconds and enable random order.",
887
+ "I've analyzed your images and will automatically crop them to consistent ratios for the best presentation.",
888
+ "To create a shorter clip, select 'Short Segment' in the video options. I'll use the first 10 images."
889
+ ];
890
+
891
+ // Random response after a delay
892
+ setTimeout(() => {
893
+ const randomResponse = responses[Math.floor(Math.random() * responses.length)];
894
+ addAIMessage(randomResponse);
895
+
896
+ // Sometimes suggest an action
897
+ if (Math.random() > 0.7) {
898
+ setTimeout(() => {
899
+ addAIMessage("Would you like me to apply these suggestions to your current slideshow settings?");
900
+ }, 1500);
901
+ }
902
+ }, 1000);
903
+ }
904
+
905
+ // Chat event listeners
906
+ chatInput.addEventListener('keypress', function(e) {
907
+ if (e.key === 'Enter') {
908
+ sendChatMessage();
909
+ }
910
+ });
911
+
912
+ sendChatBtn.addEventListener('click', sendChatMessage);
913
+
914
+ // Quick prompt buttons
915
+ quickPrompts.forEach(button => {
916
+ button.addEventListener('click', function() {
917
+ chatInput.value = this.textContent;
918
+ sendChatMessage();
919
+ });
920
+ });
921
+
922
+ // Initial AI greeting
923
+ setTimeout(() => {
924
+ addAIMessage("I see you've opened the slideshow creator! I can help you make an amazing video. Upload some images and tell me what kind of slideshow you're looking for.");
925
+ }, 1000);
926
+
927
+ // Handle video playback errors
928
+ videoPreview.addEventListener('error', function() {
929
+ showError("There was an issue playing the video. Try regenerating or downloading it instead.");
930
+ addAIMessage("Sorry about the playback issue! I recommend downloading the video to view it properly.");
931
+ });
932
+ });
933
+ </script>
934
+ <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=XXXMARK/karua" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
935
+ </html>