NeuralFalcon commited on
Commit
c38f38e
·
verified ·
1 Parent(s): 7ba2def

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +354 -22
app.py CHANGED
@@ -1,22 +1,354 @@
1
- import os
2
-
3
- video_dir = "./videos"
4
- audio_dir = "./audios"
5
-
6
- # Ensure output directory exists
7
- os.makedirs(audio_dir, exist_ok=True)
8
-
9
- for filename in os.listdir(video_dir):
10
- if filename.endswith(".mp4"):
11
- video_path = os.path.join(video_dir, filename)
12
- audio_filename = filename.replace(".mp4", ".wav")
13
- audio_path = os.path.join(audio_dir, audio_filename)
14
-
15
- command = f"ffmpeg -i \"{video_path}\" \"{audio_path}\" -y"
16
- result = os.system(command)
17
-
18
- if result == 0:
19
- print(f"✅ Audio extracted from {filename} and saved as {audio_path}")
20
- else:
21
- print(f"❌ Failed to extract audio from {filename}")
22
- print(f"Command used: {command}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en" class=""> <!-- Start without 'dark' class -->
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Italian Brinarot - Soundboard</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
+ @import url('https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap');
11
+
12
+ /* Base styles */
13
+ body {
14
+ font-family: 'Poppins', sans-serif;
15
+ /* Smooth transition for theme changes */
16
+ @apply transition-colors duration-300 ease-in-out;
17
+ }
18
+
19
+ /* Light theme base */
20
+ body {
21
+ background: linear-gradient(135deg, #f5f7fa 0%, #e4e8f0 100%);
22
+ }
23
+
24
+ /* Dark theme base */
25
+ .dark body {
26
+ background: linear-gradient(135deg, #1f2937 0%, #111827 100%); /* Dark gradient */
27
+ }
28
+
29
+ .character-card {
30
+ transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
31
+ perspective: 1000px;
32
+ @apply transition-colors duration-300 ease-in-out; /* Transition background */
33
+ }
34
+
35
+ .character-card:hover {
36
+ transform: translateY(-8px);
37
+ /* Enhanced hover shadow for both themes */
38
+ @apply shadow-xl dark:shadow-2xl dark:shadow-indigo-500/20;
39
+ }
40
+
41
+ /* Specific light/dark styles will be applied via Tailwind classes directly */
42
+
43
+ .image-container {
44
+ /* Keep the gradient or simplify */
45
+ background: linear-gradient(145deg, #ffffff, #e6e6e6);
46
+ box-shadow: 0 4px 15px rgba(0,0,0,0.08);
47
+ @apply transition-colors duration-300 ease-in-out;
48
+ }
49
+ .dark .image-container {
50
+ background: linear-gradient(145deg, #4b5563, #374151); /* Darker bg for image */
51
+ box-shadow: 0 4px 15px rgba(0,0,0,0.2);
52
+ }
53
+
54
+
55
+ .play-btn {
56
+ transition: all 0.2s ease;
57
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
58
+ }
59
+ .play-btn.playing {
60
+ background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%); /* Red gradient for playing */
61
+ }
62
+
63
+ .play-btn:hover {
64
+ transform: translateY(-2px);
65
+ box-shadow: 0 5px 15px rgba(118, 75, 162, 0.4);
66
+ }
67
+ .play-btn.playing:hover {
68
+ box-shadow: 0 5px 15px rgba(239, 68, 68, 0.4); /* Red shadow */
69
+ }
70
+
71
+ .play-btn:active {
72
+ transform: translateY(0);
73
+ }
74
+
75
+ .gradient-text {
76
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
77
+ -webkit-background-clip: text;
78
+ background-clip: text;
79
+ color: transparent;
80
+ }
81
+ /* Optional: Slightly adjust gradient text for dark mode if needed */
82
+ /* .dark .gradient-text { ... } */
83
+
84
+
85
+ .header-divider {
86
+ height: 4px;
87
+ background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);
88
+ @apply transition-all duration-300 ease-in-out;
89
+ }
90
+ .dark .header-divider {
91
+ background: linear-gradient(90deg, #818cf8 0%, #a78bfa 100%); /* Brighter gradient for dark */
92
+ }
93
+
94
+ /* Theme toggle button */
95
+ #theme-toggle {
96
+ @apply fixed top-5 right-5 z-50;
97
+ @apply w-12 h-12 rounded-full ;
98
+ @apply flex items-center justify-center;
99
+ @apply bg-white/80 dark:bg-gray-800/80 backdrop-blur-sm; /* Semi-transparent background */
100
+ @apply text-gray-600 dark:text-gray-300;
101
+ @apply hover:bg-white dark:hover:bg-gray-700;
102
+ @apply shadow-md hover:shadow-lg;
103
+ @apply transition-all duration-300 ease-in-out;
104
+ @apply cursor-pointer;
105
+ }
106
+ #theme-toggle i {
107
+ @apply text-xl;
108
+ }
109
+
110
+ @media (max-width: 640px) {
111
+ .character-name {
112
+ font-size: 1rem; /* Already adjusted */
113
+ }
114
+ #theme-toggle {
115
+ @apply top-3 right-3 w-10 h-10; /* Smaller on mobile */
116
+ }
117
+ #theme-toggle i {
118
+ @apply text-lg;
119
+ }
120
+ }
121
+ </style>
122
+ </head>
123
+ <body class="min-h-screen"> <!-- Body class will be updated by JS -->
124
+
125
+ <!-- Theme Toggle Button -->
126
+ <button id="theme-toggle" aria-label="Toggle dark mode">
127
+ <i class="fas fa-moon"></i> <!-- Moon icon for light theme -->
128
+ <i class="fas fa-sun hidden"></i> <!-- Sun icon for dark theme -->
129
+ </button>
130
+
131
+ <div class="container mx-auto px-4 py-12 md:py-16">
132
+ <!-- Header -->
133
+ <header class="text-center mb-12 md:mb-16">
134
+ <h1 class="text-4xl md:text-5xl lg:text-6xl font-bold gradient-text mb-3">Italian Brinarot</h1>
135
+ <div class="header-divider w-32 md:w-40 mx-auto rounded-full mb-6"></div>
136
+ <p class="text-lg text-gray-600 dark:text-gray-400 max-w-2xl mx-auto">
137
+ Discover the magical sounds of Italian Brinarot characters
138
+ </p>
139
+ </header>
140
+
141
+ <!-- Character Grid -->
142
+ <div id="characterGrid" class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-8">
143
+ <!-- Character cards will be injected here by JS -->
144
+ </div>
145
+ </div>
146
+
147
+ <!-- Embedded Character Data -->
148
+ <script>
149
+ const characterData = {
150
+ // ... (Keep your characterData object exactly the same)
151
+ "Bombardiro Crocodilo": { "image_path": "./assets/Bombardiro_Crocodilo.png", "audio_path": "./assets/Bombardiro_Crocodilo.wav" },
152
+ "Tralalero Tralala": { "image_path": "./assets/Tralalero_Tralala.png", "audio_path": "./assets/Tralalero_Tralala.wav" },
153
+ "Bombombini Gusini": { "image_path": "./assets/Bombombini_Gusini.png", "audio_path": "./assets/Bombombini_Gusini.wav" },
154
+ "Tung Tung Tung Tung Tung Tung Tung Tung Tung Sahur": { "image_path": "./assets/Tung_Tung_Tung_Tung_Tung_Tung_Tung_Tung_Tung_Sahur.png", "audio_path": "./assets/Tung_Tung_Tung_Tung_Tung_Tung_Tung_Tung_Tung_Sahur.wav" },
155
+ "Brr Brr Patapim": { "image_path": "./assets/Brr_Brr_Patapim.png", "audio_path": "./assets/Brr_Brr_Patapim.wav" },
156
+ "Cappuccino Assassino": { "image_path": "./assets/Cappuccino_Assassino.png", "audio_path": "./assets/Cappuccino_Assassino.wav" },
157
+ "Lirili Larila": { "image_path": "./assets/Lirili_Larila.png", "audio_path": "./assets/Lirili_Larila.wav" },
158
+ "Trulimero Trulichina": { "image_path": "./assets/Trulimero_Trulichina.png", "audio_path": "./assets/Trulimero_Trulichina.wav" },
159
+ "Boneca Ambalabu": { "image_path": "./assets/Boneca_Ambalabu.png", "audio_path": "./assets/Boneca_Ambalabu.wav" },
160
+ "Chimpanzini Bananini": { "image_path": "./assets/Chimpanzini_Bananini.png", "audio_path": "./assets/Chimpanzini_Bananini.wav" },
161
+ "Bananita Dolfinita": { "image_path": "./assets/Bananita_Dolfinita.png", "audio_path": "./assets/Bananita_Dolfinita.wav" },
162
+ "Bluberini Octopusini": { "image_path": "./assets/Bluberini_Octopusini.png", "audio_path": "./assets/Bluberini_Octopusini.wav" },
163
+ "Bobrito Bandito": { "image_path": "./assets/Bobrito_Bandito.png", "audio_path": "./assets/Bobrito_Bandito.wav" },
164
+ "Brri Brri Bicus Dicus": { "image_path": "./assets/Brri_Brri_Bicus_Dicus.png", "audio_path": "./assets/Brri_Brri_Bicus_Dicus.wav" },
165
+ "Burbaloni Luliloli": { "image_path": "./assets/Burbaloni_Luliloli.png", "audio_path": "./assets/Burbaloni_Luliloli.wav" },
166
+ "Chimpanzini Annasini": { "image_path": "./assets/Chimpanzini_Annasini.png", "audio_path": "./assets/Chimpanzini_Annasini.wav" },
167
+ "Chimpanzini Cocosini": { "image_path": "./assets/Chimpanzini_Cocosini.png", "audio_path": "./assets/Chimpanzini_Cocosini.wav" },
168
+ "Cocofanto Elefanto": { "image_path": "./assets/Cocofanto_Elefanto.png", "audio_path": "./assets/Cocofanto_Elefanto.wav" },
169
+ "Frigo Camelo Buffardello": { "image_path": "./assets/frigo_camelo_buffardello.png", "audio_path": "./assets/frigo_camelo_buffardello.wav" },
170
+ "Giraffe Celeste": { "image_path": "./assets/Giraffe_Celeste.png", "audio_path": "./assets/Giraffe_Celeste.wav" },
171
+ "Glorbo Fruttodrillo": { "image_path": "./assets/Glorbo_Fruttodrillo.png", "audio_path": "./assets/Glorbo_Fruttodrillo.wav" },
172
+ "Il Cacto Hipopotoma": { "image_path": "./assets/il_cacto_hipopotoma.png", "audio_path": "./assets/il_cacto_hipopotoma.wav" },
173
+ "Trippi Troppi Troppa Trippa": { "image_path": "./assets/Trippi_Troppi_Troppa_Trippa.png", "audio_path": "./assets/Trippi_Troppi_Troppa_Trippa.wav" }
174
+ };
175
+
176
+ const characterGrid = document.getElementById('characterGrid');
177
+ const themeToggleButton = document.getElementById('theme-toggle');
178
+ const sunIcon = themeToggleButton.querySelector('.fa-sun');
179
+ const moonIcon = themeToggleButton.querySelector('.fa-moon');
180
+ const htmlElement = document.documentElement; // Target <html> for dark class
181
+
182
+ let currentlyPlayingAudio = null;
183
+ let currentlyPlayingButton = null;
184
+
185
+ // Function to apply the theme
186
+ function applyTheme(theme) {
187
+ if (theme === 'dark') {
188
+ htmlElement.classList.add('dark');
189
+ moonIcon.classList.add('hidden');
190
+ sunIcon.classList.remove('hidden');
191
+ localStorage.setItem('theme', 'dark');
192
+ } else {
193
+ htmlElement.classList.remove('dark');
194
+ moonIcon.classList.remove('hidden');
195
+ sunIcon.classList.add('hidden');
196
+ localStorage.setItem('theme', 'light');
197
+ }
198
+ }
199
+
200
+ // Function to toggle the theme
201
+ function toggleTheme() {
202
+ const currentTheme = htmlElement.classList.contains('dark') ? 'dark' : 'light';
203
+ applyTheme(currentTheme === 'dark' ? 'light' : 'dark');
204
+ }
205
+
206
+ // Function to load and display characters
207
+ function loadCharacters() {
208
+ characterGrid.innerHTML = ''; // Clear existing
209
+
210
+ Object.entries(characterData).forEach(([name, data]) => {
211
+ const card = document.createElement('div');
212
+ // Apply base and dark mode classes using Tailwind
213
+ card.className = `
214
+ character-card
215
+ bg-white dark:bg-gray-800
216
+ rounded-2xl overflow-hidden
217
+ shadow-md hover:shadow-xl dark:hover:shadow-2xl dark:hover:shadow-indigo-500/20
218
+ border border-gray-200 dark:border-gray-700/50
219
+ flex flex-col items-center p-6
220
+ transition-all duration-300 ease-in-out
221
+ `;
222
+
223
+ // Image container
224
+ const imageContainer = document.createElement('div');
225
+ imageContainer.className = `
226
+ image-container mb-5 w-40 h-40 lg:w-44 lg:h-44
227
+ flex items-center justify-center
228
+ rounded-2xl overflow-hidden
229
+ border-4 border-white/50 dark:border-gray-700/50
230
+ `; // Added slightly larger size on larger screens and border
231
+
232
+ const image = document.createElement('img');
233
+ image.src = data.image_path.replace(/\\/g, '/');
234
+ image.alt = name;
235
+ image.className = 'w-full h-full object-cover';
236
+ image.onerror = () => { // Basic error handling for images
237
+ image.src = 'placeholder.png'; // Provide a fallback image path
238
+ console.warn(`Image not found: ${data.image_path}`);
239
+ };
240
+ imageContainer.appendChild(image);
241
+
242
+ // Character name
243
+ const nameElement = document.createElement('h3');
244
+ nameElement.className = `
245
+ character-name text-lg md:text-xl font-semibold
246
+ text-gray-800 dark:text-gray-100
247
+ mb-4 text-center transition-colors duration-300
248
+ `;
249
+ nameElement.textContent = name;
250
+
251
+ // Play/Pause button
252
+ const playButton = document.createElement('button');
253
+ playButton.className = `
254
+ play-btn text-white font-medium py-2.5 px-7
255
+ rounded-full flex items-center justify-center
256
+ w-32 /* Fixed width for consistency */
257
+ `;
258
+
259
+ const playIcon = document.createElement('i');
260
+ playIcon.className = 'fas fa-play mr-2'; // Start with play
261
+
262
+ playButton.appendChild(playIcon);
263
+ playButton.appendChild(document.createTextNode('Play'));
264
+
265
+ // Audio element (keep it hidden)
266
+ const audio = new Audio(data.audio_path.replace(/\\/g, '/'));
267
+ audio.preload = 'metadata'; // Improve loading slightly
268
+ audio.style.display = 'none';
269
+ audio.onerror = () => {
270
+ console.error(`Error loading audio: ${data.audio_path}`);
271
+ playButton.disabled = true; // Disable button if audio fails
272
+ playButton.textContent = 'Error';
273
+ playButton.classList.add('opacity-50', 'cursor-not-allowed');
274
+ };
275
+
276
+
277
+ // --- Refined Play/Pause Logic ---
278
+ playButton.addEventListener('click', () => {
279
+ // If this audio is currently playing, pause it
280
+ if (currentlyPlayingAudio === audio) {
281
+ audio.pause();
282
+ currentlyPlayingAudio = null;
283
+ currentlyPlayingButton = null;
284
+ // Reset this button visually
285
+ playButton.innerHTML = '<i class="fas fa-play mr-2"></i>Play';
286
+ playButton.classList.remove('playing');
287
+ } else {
288
+ // If another audio is playing, stop it first
289
+ if (currentlyPlayingAudio) {
290
+ currentlyPlayingAudio.pause();
291
+ currentlyPlayingAudio.currentTime = 0; // Reset time
292
+ // Reset the *other* button visually
293
+ if (currentlyPlayingButton) {
294
+ currentlyPlayingButton.innerHTML = '<i class="fas fa-play mr-2"></i>Play';
295
+ currentlyPlayingButton.classList.remove('playing');
296
+ }
297
+ }
298
+ // Now play this audio
299
+ audio.currentTime = 0;
300
+ audio.play().catch(e => console.error("Audio play failed:", e)); // Catch potential play errors
301
+ currentlyPlayingAudio = audio;
302
+ currentlyPlayingButton = playButton;
303
+ // Update this button visually to 'Pause'
304
+ playButton.innerHTML = '<i class="fas fa-pause mr-2"></i>Pause';
305
+ playButton.classList.add('playing');
306
+ }
307
+ });
308
+
309
+ // When audio naturally ends, reset the button
310
+ audio.addEventListener('ended', () => {
311
+ if (currentlyPlayingAudio === audio) { // Ensure it's the one that just ended
312
+ playButton.innerHTML = '<i class="fas fa-play mr-2"></i>Play';
313
+ playButton.classList.remove('playing');
314
+ currentlyPlayingAudio = null;
315
+ currentlyPlayingButton = null;
316
+ }
317
+ });
318
+ // --- End Refined Play/Pause Logic ---
319
+
320
+
321
+ // Assemble the card
322
+ card.appendChild(imageContainer);
323
+ card.appendChild(nameElement);
324
+ card.appendChild(playButton);
325
+ card.appendChild(audio); // Add hidden audio element
326
+
327
+ // Add card to the grid
328
+ characterGrid.appendChild(card);
329
+ });
330
+ }
331
+
332
+ // --- Initialization ---
333
+
334
+ // 1. Set initial theme based on localStorage or system preference
335
+ const savedTheme = localStorage.getItem('theme');
336
+ const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
337
+
338
+ if (savedTheme) {
339
+ applyTheme(savedTheme);
340
+ } else if (prefersDark) {
341
+ applyTheme('dark');
342
+ } else {
343
+ applyTheme('light'); // Default to light
344
+ }
345
+
346
+ // 2. Add listener for the theme toggle button
347
+ themeToggleButton.addEventListener('click', toggleTheme);
348
+
349
+ // 3. Load characters when the page loads
350
+ window.addEventListener('DOMContentLoaded', loadCharacters);
351
+
352
+ </script>
353
+ </body>
354
+ </html>