EGuihaire commited on
Commit
562b286
·
verified ·
1 Parent(s): b83beff

Add 2 files

Browse files
Files changed (2) hide show
  1. README.md +7 -5
  2. index.html +402 -19
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
- title: Paris Space
3
- emoji: 🐢
4
- colorFrom: purple
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: paris-space
3
+ emoji: 🐳
4
+ colorFrom: pink
5
+ colorTo: red
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,402 @@
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="fr">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Transcription Audio avec GPT-4</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <script src="https://unpkg.com/dropzone@5/dist/min/dropzone.min.js"></script>
9
+ <link rel="stylesheet" href="https://unpkg.com/dropzone@5/dist/min/dropzone.min.css" type="text/css" />
10
+ <style>
11
+ .dropzone {
12
+ border: 2px dashed #3b82f6;
13
+ border-radius: 0.5rem;
14
+ background: #f8fafc;
15
+ min-height: 200px;
16
+ padding: 20px;
17
+ }
18
+ .dropzone .dz-message {
19
+ font-size: 1.25rem;
20
+ color: #64748b;
21
+ }
22
+ .progress-bar {
23
+ height: 6px;
24
+ background-color: #e2e8f0;
25
+ border-radius: 3px;
26
+ margin-top: 10px;
27
+ }
28
+ .progress-bar-fill {
29
+ height: 100%;
30
+ border-radius: 3px;
31
+ background-color: #3b82f6;
32
+ width: 0%;
33
+ transition: width 0.3s ease;
34
+ }
35
+ .file-item {
36
+ transition: all 0.3s ease;
37
+ }
38
+ .file-item:hover {
39
+ transform: translateY(-2px);
40
+ box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
41
+ }
42
+ .loader {
43
+ border: 3px solid #f3f3f3;
44
+ border-top: 3px solid #3b82f6;
45
+ border-radius: 50%;
46
+ width: 20px;
47
+ height: 20px;
48
+ animation: spin 1s linear infinite;
49
+ display: inline-block;
50
+ vertical-align: middle;
51
+ margin-right: 8px;
52
+ }
53
+ @keyframes spin {
54
+ 0% { transform: rotate(0deg); }
55
+ 100% { transform: rotate(360deg); }
56
+ }
57
+ </style>
58
+ </head>
59
+ <body class="bg-gray-50 min-h-screen">
60
+ <div class="container mx-auto px-4 py-8 max-w-4xl">
61
+ <div class="text-center mb-8">
62
+ <h1 class="text-3xl font-bold text-gray-800 mb-2">Transcription Audio avec GPT-4</h1>
63
+ <p class="text-gray-600">Transcrivez vos fichiers audio en texte avec une précision exceptionnelle</p>
64
+ </div>
65
+
66
+ <div class="bg-white rounded-xl shadow-md p-6 mb-8">
67
+ <div class="mb-6">
68
+ <label for="api-key" class="block text-sm font-medium text-gray-700 mb-1">Clé API OpenAI</label>
69
+ <div class="flex">
70
+ <input type="password" id="api-key" placeholder="sk-..."
71
+ class="flex-1 px-4 py-2 border border-gray-300 rounded-l-md focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
72
+ <button id="toggle-key" class="px-4 py-2 bg-gray-100 border border-l-0 border-gray-300 rounded-r-md hover:bg-gray-200">
73
+ 👁️
74
+ </button>
75
+ </div>
76
+ <p class="mt-1 text-xs text-gray-500">Votre clé API n'est jamais envoyée à nos serveurs</p>
77
+ </div>
78
+
79
+ <div class="mb-4">
80
+ <label class="block text-sm font-medium text-gray-700 mb-2">Options de transcription</label>
81
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
82
+ <div>
83
+ <label for="language" class="block text-xs text-gray-500 mb-1">Langue</label>
84
+ <select id="language" class="w-full px-4 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
85
+ <option value="fr">Français</option>
86
+ <option value="en">Anglais</option>
87
+ <option value="es">Espagnol</option>
88
+ <option value="de">Allemand</option>
89
+ <option value="it">Italien</option>
90
+ </select>
91
+ </div>
92
+ <div>
93
+ <label for="model" class="block text-xs text-gray-500 mb-1">Modèle</label>
94
+ <select id="model" class="w-full px-4 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
95
+ <option value="whisper-1">Whisper (optimisé pour audio)</option>
96
+ <option value="gpt-4o">GPT-4 Omni (meilleure précision)</option>
97
+ </select>
98
+ </div>
99
+ </div>
100
+ </div>
101
+
102
+ <div id="dropzone" class="dropzone">
103
+ <div class="dz-message" data-dz-message>
104
+ <div class="flex flex-col items-center justify-center">
105
+ <svg class="w-12 h-12 text-blue-500 mb-2" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
106
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12"></path>
107
+ </svg>
108
+ <span>Glissez-déposez vos fichiers audio ici</span>
109
+ <span class="text-sm text-gray-500 mt-1">ou cliquez pour sélectionner</span>
110
+ </div>
111
+ </div>
112
+ </div>
113
+
114
+ <div class="mt-4">
115
+ <div class="flex justify-between items-center mb-2">
116
+ <span class="text-sm font-medium text-gray-700">Fichiers en attente</span>
117
+ <button id="transcribe-all" class="px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 disabled:opacity-50 disabled:cursor-not-allowed" disabled>
118
+ Transcrire tout
119
+ </button>
120
+ </div>
121
+ <div id="file-list" class="space-y-2">
122
+ <!-- Les fichiers apparaîtront ici -->
123
+ </div>
124
+ </div>
125
+ </div>
126
+
127
+ <div id="results" class="bg-white rounded-xl shadow-md p-6 hidden">
128
+ <div class="flex justify-between items-center mb-4">
129
+ <h2 class="text-xl font-semibold text-gray-800">Résultats de transcription</h2>
130
+ <button id="copy-all" class="px-3 py-1 bg-gray-100 text-gray-700 rounded-md text-sm hover:bg-gray-200">
131
+ Copier tout
132
+ </button>
133
+ </div>
134
+ <div id="transcription-results" class="space-y-4">
135
+ <!-- Les résultats apparaîtront ici -->
136
+ </div>
137
+ </div>
138
+ </div>
139
+
140
+ <script>
141
+ document.addEventListener('DOMContentLoaded', function() {
142
+ // Toggle API key visibility
143
+ const apiKeyInput = document.getElementById('api-key');
144
+ const toggleKeyBtn = document.getElementById('toggle-key');
145
+
146
+ toggleKeyBtn.addEventListener('click', function() {
147
+ if (apiKeyInput.type === 'password') {
148
+ apiKeyInput.type = 'text';
149
+ toggleKeyBtn.textContent = '🔒';
150
+ } else {
151
+ apiKeyInput.type = 'password';
152
+ toggleKeyBtn.textContent = '👁️';
153
+ }
154
+ });
155
+
156
+ // Initialize Dropzone
157
+ Dropzone.autoDiscover = false;
158
+ const myDropzone = new Dropzone("#dropzone", {
159
+ url: "/fake-url", // We'll handle the upload manually
160
+ paramName: "file",
161
+ maxFilesize: 50, // MB
162
+ acceptedFiles: "audio/*,video/*,.mp3,.wav,.m4a,.mp4,.ogg",
163
+ addRemoveLinks: true,
164
+ autoProcessQueue: false,
165
+ dictDefaultMessage: "",
166
+ dictFallbackMessage: "Votre navigateur ne supporte pas le glisser-déposer de fichiers.",
167
+ dictFileTooBig: "Fichier trop volumineux ({{filesize}}MB). Taille maximale: {{maxFilesize}}MB.",
168
+ dictInvalidFileType: "Type de fichier non supporté.",
169
+ dictResponseError: "Le serveur a répondu avec le code {{statusCode}}.",
170
+ dictCancelUpload: "Annuler",
171
+ dictUploadCanceled: "Téléchargement annulé.",
172
+ dictRemoveFile: "Supprimer",
173
+ dictMaxFilesExceeded: "Vous ne pouvez pas télécharger plus de fichiers."
174
+ });
175
+
176
+ const fileList = document.getElementById('file-list');
177
+ const transcribeAllBtn = document.getElementById('transcribe-all');
178
+ const resultsSection = document.getElementById('results');
179
+ const transcriptionResults = document.getElementById('transcription-results');
180
+ const copyAllBtn = document.getElementById('copy-all');
181
+
182
+ let filesToTranscribe = [];
183
+
184
+ // Handle added files
185
+ myDropzone.on("addedfile", function(file) {
186
+ filesToTranscribe.push(file);
187
+ updateFileList();
188
+ transcribeAllBtn.disabled = filesToTranscribe.length === 0;
189
+ });
190
+
191
+ // Handle removed files
192
+ myDropzone.on("removedfile", function(file) {
193
+ filesToTranscribe = filesToTranscribe.filter(f => f !== file);
194
+ updateFileList();
195
+ transcribeAllBtn.disabled = filesToTranscribe.length === 0;
196
+
197
+ // Remove from DOM if exists
198
+ const fileElement = document.getElementById(`file-${file.upload.uuid}`);
199
+ if (fileElement) {
200
+ fileElement.remove();
201
+ }
202
+ });
203
+
204
+ // Update the file list UI
205
+ function updateFileList() {
206
+ fileList.innerHTML = '';
207
+
208
+ filesToTranscribe.forEach(file => {
209
+ const fileElement = document.createElement('div');
210
+ fileElement.id = `file-${file.upload.uuid}`;
211
+ fileElement.className = 'file-item bg-gray-50 rounded-lg p-3 flex justify-between items-center';
212
+
213
+ fileElement.innerHTML = `
214
+ <div class="flex items-center">
215
+ <svg class="w-5 h-5 text-blue-500 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
216
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19V6l12-3v13M9 19c0 1.105-1.343 2-3 2s-3-.895-3-2 1.343-2 3-2 3 .895 3 2zm12-3c0 1.105-1.343 2-3 2s-3-.895-3-2 1.343-2 3-2 3 .895 3 2zM9 10l12-3"></path>
217
+ </svg>
218
+ <span class="text-sm font-medium">${file.name}</span>
219
+ <span class="text-xs text-gray-500 ml-2">(${(file.size / (1024 * 1024)).toFixed(2)} MB)</span>
220
+ </div>
221
+ <div class="flex items-center">
222
+ <button class="transcribe-btn px-3 py-1 bg-blue-600 text-white rounded-md text-sm hover:bg-blue-700 mr-2" data-file-id="${file.upload.uuid}">
223
+ Transcrire
224
+ </button>
225
+ <button class="remove-btn text-gray-500 hover:text-red-500" data-file-id="${file.upload.uuid}">
226
+ <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
227
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"></path>
228
+ </svg>
229
+ </button>
230
+ </div>
231
+ `;
232
+
233
+ fileList.appendChild(fileElement);
234
+
235
+ // Add event listeners to the buttons
236
+ fileElement.querySelector('.transcribe-btn').addEventListener('click', () => transcribeFile(file));
237
+ fileElement.querySelector('.remove-btn').addEventListener('click', () => {
238
+ myDropzone.removeFile(file);
239
+ });
240
+ });
241
+ }
242
+
243
+ // Transcribe a single file
244
+ async function transcribeFile(file) {
245
+ const apiKey = apiKeyInput.value.trim();
246
+ if (!apiKey) {
247
+ alert('Veuillez entrer votre clé API OpenAI');
248
+ return;
249
+ }
250
+
251
+ const language = document.getElementById('language').value;
252
+ const model = document.getElementById('model').value;
253
+
254
+ const fileElement = document.getElementById(`file-${file.upload.uuid}`);
255
+ if (!fileElement) return;
256
+
257
+ const transcribeBtn = fileElement.querySelector('.transcribe-btn');
258
+ transcribeBtn.disabled = true;
259
+ transcribeBtn.innerHTML = '<span class="loader"></span> Traitement...';
260
+
261
+ try {
262
+ // In a real app, you would upload the file to your server first
263
+ // Then your server would call the OpenAI API
264
+ // Here we're simulating the process with a timeout
265
+
266
+ // Simulate upload progress
267
+ let progress = 0;
268
+ const progressInterval = setInterval(() => {
269
+ progress += Math.random() * 10;
270
+ if (progress >= 90) clearInterval(progressInterval);
271
+ transcribeBtn.innerHTML = `<span class="loader"></span> Upload ${Math.min(100, Math.round(progress))}%`;
272
+ }, 300);
273
+
274
+ // Simulate API call to OpenAI
275
+ const response = await simulateOpenAICall(apiKey, file, language, model);
276
+
277
+ clearInterval(progressInterval);
278
+ transcribeBtn.innerHTML = 'Terminé ✓';
279
+
280
+ // Show results
281
+ showTranscriptionResult(file.name, response);
282
+
283
+ // Remove file from list after a delay
284
+ setTimeout(() => {
285
+ myDropzone.removeFile(file);
286
+ }, 2000);
287
+
288
+ } catch (error) {
289
+ console.error('Error:', error);
290
+ transcribeBtn.innerHTML = 'Erreur';
291
+ alert('Une erreur est survenue lors de la transcription: ' + error.message);
292
+ }
293
+ }
294
+
295
+ // Simulate OpenAI API call (replace with actual API call in production)
296
+ function simulateOpenAICall(apiKey, file, language, model) {
297
+ return new Promise((resolve) => {
298
+ setTimeout(() => {
299
+ // This is a mock response - in a real app you would call:
300
+ // const formData = new FormData();
301
+ // formData.append('file', file);
302
+ // formData.append('model', 'whisper-1');
303
+ // formData.append('language', language);
304
+
305
+ // const response = await fetch('https://api.openai.com/v1/audio/transcriptions', {
306
+ // method: 'POST',
307
+ // headers: {
308
+ // 'Authorization': `Bearer ${apiKey}`
309
+ // },
310
+ // body: formData
311
+ // });
312
+
313
+ // const data = await response.json();
314
+
315
+ // Mock data based on file name
316
+ const mockText = `Ceci est une transcription simulée du fichier ${file.name}.
317
+
318
+ Les systèmes de transcription automatique comme Whisper et GPT-4 offrent des résultats de plus en plus précis. Cette démo montre l'interface utilisateur, mais dans une application réelle, vous appelleriez l'API OpenAI pour obtenir une vraie transcription.
319
+
320
+ La qualité de la transcription dépend de plusieurs facteurs :
321
+ - La qualité de l'enregistrement audio
322
+ - La présence de bruit de fond
323
+ - La complexité du vocabulaire utilisé
324
+ - L'accent des locuteurs
325
+
326
+ Pour une meilleure précision, utilisez des fichiers audio de haute qualité et spécifiez la langue correcte.`;
327
+
328
+ resolve({ text: mockText });
329
+ }, 3000);
330
+ });
331
+ }
332
+
333
+ // Show transcription result
334
+ function showTranscriptionResult(filename, result) {
335
+ resultsSection.classList.remove('hidden');
336
+
337
+ const resultElement = document.createElement('div');
338
+ resultElement.className = 'bg-gray-50 rounded-lg p-4';
339
+ resultElement.innerHTML = `
340
+ <div class="flex justify-between items-center mb-2">
341
+ <h3 class="font-medium text-blue-600">${filename}</h3>
342
+ <button class="copy-btn px-2 py-1 bg-gray-200 text-gray-700 rounded text-xs hover:bg-gray-300 flex items-center">
343
+ <svg class="w-4 h-4 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
344
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 5H6a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2v-1M8 5a2 2 0 002 2h2a2 2 0 002-2M8 5a2 2 0 012-2h2a2 2 0 012 2m0 0h2a2 2 0 012 2v3m2 4H10m0 0l3-3m-3 3l3 3"></path>
345
+ </svg>
346
+ Copier
347
+ </button>
348
+ </div>
349
+ <div class="text-sm text-gray-700 whitespace-pre-line transcription-text">${result.text}</div>
350
+ `;
351
+
352
+ transcriptionResults.appendChild(resultElement);
353
+
354
+ // Add copy button functionality
355
+ resultElement.querySelector('.copy-btn').addEventListener('click', () => {
356
+ const textToCopy = resultElement.querySelector('.transcription-text').textContent;
357
+ navigator.clipboard.writeText(textToCopy).then(() => {
358
+ const btn = resultElement.querySelector('.copy-btn');
359
+ btn.innerHTML = '<svg class="w-4 h-4 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path></svg> Copié!';
360
+ setTimeout(() => {
361
+ btn.innerHTML = '<svg class="w-4 h-4 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 5H6a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2v-1M8 5a2 2 0 002 2h2a2 2 0 002-2M8 5a2 2 0 012-2h2a2 2 0 012 2m0 0h2a2 2 0 012 2v3m2 4H10m0 0l3-3m-3 3l3 3"></path></svg> Copier';
362
+ }, 2000);
363
+ });
364
+ });
365
+ }
366
+
367
+ // Transcribe all files
368
+ transcribeAllBtn.addEventListener('click', async () => {
369
+ const apiKey = apiKeyInput.value.trim();
370
+ if (!apiKey) {
371
+ alert('Veuillez entrer votre clé API OpenAI');
372
+ return;
373
+ }
374
+
375
+ transcribeAllBtn.disabled = true;
376
+ transcribeAllBtn.innerHTML = '<span class="loader"></span> Traitement...';
377
+
378
+ // Process files sequentially
379
+ for (const file of [...filesToTranscribe]) {
380
+ await transcribeFile(file);
381
+ }
382
+
383
+ transcribeAllBtn.innerHTML = 'Tout transcrire';
384
+ });
385
+
386
+ // Copy all transcriptions
387
+ copyAllBtn.addEventListener('click', () => {
388
+ const allTexts = Array.from(document.querySelectorAll('.transcription-text'))
389
+ .map(el => el.textContent)
390
+ .join('\n\n');
391
+
392
+ navigator.clipboard.writeText(allTexts).then(() => {
393
+ copyAllBtn.textContent = 'Tout copié!';
394
+ setTimeout(() => {
395
+ copyAllBtn.textContent = 'Copier tout';
396
+ }, 2000);
397
+ });
398
+ });
399
+ });
400
+ </script>
401
+ <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=EGuihaire/paris-space" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
402
+ </html>