Ultronprime commited on
Commit
faf8d87
·
verified ·
1 Parent(s): b531a8a

Add 3 files

Browse files
Files changed (3) hide show
  1. README.md +7 -5
  2. index.html +350 -19
  3. prompts.txt +0 -0
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
- title: Extension Changer
3
- emoji: 🐢
4
- colorFrom: purple
5
- colorTo: purple
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: extension-changer
3
+ emoji: 🐳
4
+ colorFrom: blue
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,350 @@
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>Bulk File Extension Changer</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js"></script>
9
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2.0.5/FileSaver.min.js"></script>
10
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
11
+ <style>
12
+ .dropzone {
13
+ border: 2px dashed #94a3b8;
14
+ transition: all 0.3s ease;
15
+ }
16
+ .dropzone.active {
17
+ border-color: #4f46e5;
18
+ background-color: #eef2ff;
19
+ }
20
+ .file-item:hover .remove-btn {
21
+ opacity: 1;
22
+ }
23
+ @keyframes fadeIn {
24
+ from { opacity: 0; transform: translateY(10px); }
25
+ to { opacity: 1; transform: translateY(0); }
26
+ }
27
+ .fade-in {
28
+ animation: fadeIn 0.3s ease forwards;
29
+ }
30
+ .progress-bar {
31
+ transition: width 0.3s ease;
32
+ }
33
+ #zipLoading {
34
+ display: none;
35
+ }
36
+ </style>
37
+ </head>
38
+ <body class="bg-gray-50 min-h-screen">
39
+ <div class="container mx-auto px-4 py-12 max-w-4xl">
40
+ <div class="text-center mb-12">
41
+ <h1 class="text-4xl font-bold text-indigo-600 mb-3">Bulk File Extension Changer</h1>
42
+ <p class="text-lg text-gray-600">Select multiple files and change their extensions in one click</p>
43
+ </div>
44
+
45
+ <div class="bg-white rounded-xl shadow-lg overflow-hidden">
46
+ <!-- File Drop Zone -->
47
+ <div id="dropzone" class="dropzone p-10 text-center cursor-pointer rounded-t-xl">
48
+ <div class="mx-auto max-w-md">
49
+ <div class="flex justify-center mb-4">
50
+ <i class="fas fa-cloud-upload-alt text-5xl text-indigo-400"></i>
51
+ </div>
52
+ <h3 class="text-xl font-medium text-gray-700 mb-2">Drag & drop files here</h3>
53
+ <p class="text-gray-500 mb-4">or</p>
54
+ <label for="fileInput" class="inline-flex items-center px-6 py-3 bg-indigo-600 hover:bg-indigo-700 text-white font-medium rounded-lg shadow-sm cursor-pointer transition">
55
+ <i class="fas fa-folder-open mr-3"></i>
56
+ Browse Files
57
+ <input id="fileInput" type="file" multiple class="hidden">
58
+ </label>
59
+ <p class="text-sm text-gray-400 mt-4">Supports all file types</p>
60
+ </div>
61
+ </div>
62
+
63
+ <!-- Control Panel -->
64
+ <div class="bg-gray-50 p-6 border-t border-b border-gray-200">
65
+ <div class="flex flex-col sm:flex-row sm:items-center gap-4">
66
+ <div class="flex-1">
67
+ <label for="newExtension" class="block text-sm font-medium text-gray-700 mb-1">New Extension</label>
68
+ <div class="flex rounded-md shadow-sm">
69
+ <span class="inline-flex items-center px-3 rounded-l-md border border-r-0 border-gray-300 bg-gray-50 text-gray-500 text-sm">.</span>
70
+ <input type="text" id="newExtension" class="focus:ring-indigo-500 focus:border-indigo-500 flex-1 block w-full rounded-none rounded-r-md sm:text-sm border-gray-300 border" placeholder="jpg, png, txt..." value="">
71
+ </div>
72
+ </div>
73
+ <div class="mt-4 sm:mt-0">
74
+ <button id="renameBtn" class="inline-flex items-center px-6 py-3 bg-indigo-600 hover:bg-indigo-700 text-white font-medium rounded-lg shadow-sm cursor-pointer transition disabled:opacity-50 disabled:cursor-not-allowed" disabled>
75
+ <i class="fas fa-exchange-alt mr-3"></i>
76
+ Change Extensions
77
+ </button>
78
+ </div>
79
+ </div>
80
+ </div>
81
+
82
+ <!-- Selected Files List -->
83
+ <div id="filesList" class="divide-y divide-gray-200 max-h-96 overflow-y-auto">
84
+ <!-- Files will be listed here -->
85
+ <div class="py-4 px-6 text-center text-gray-500" id="emptyState">
86
+ <i class="fas fa-files-empty text-3xl mb-3 text-gray-300"></i>
87
+ <p>No files selected</p>
88
+ </div>
89
+ </div>
90
+
91
+ <!-- Download Buttons -->
92
+ <div id="downloadSection" class="bg-emerald-50 p-6 text-center hidden">
93
+ <h3 class="text-lg font-medium text-emerald-800 mb-3">Files processed successfully!</h3>
94
+ <div class="flex flex-col sm:flex-row justify-center gap-4">
95
+ <button id="downloadAllBtn" class="inline-flex items-center px-6 py-3 bg-emerald-600 hover:bg-emerald-700 text-white font-medium rounded-lg shadow-sm cursor-pointer transition">
96
+ <i class="fas fa-download mr-3"></i>
97
+ Download All Files
98
+ </button>
99
+ <button id="downloadZipBtn" class="inline-flex items-center px-6 py-3 bg-indigo-600 hover:bg-indigo-700 text-white font-medium rounded-lg shadow-sm cursor-pointer transition">
100
+ <i class="fas fa-file-archive mr-3"></i>
101
+ Download as ZIP
102
+ <div id="zipLoading" class="ml-3">
103
+ <div class="w-4 h-4 border-2 border-white border-t-transparent rounded-full animate-spin"></div>
104
+ </div>
105
+ </button>
106
+ </div>
107
+ <div id="zipProgress" class="mt-4 hidden">
108
+ <div class="w-full bg-gray-200 rounded-full h-2.5">
109
+ <div class="bg-emerald-600 h-2.5 rounded-full progress-bar" style="width: 0%"></div>
110
+ </div>
111
+ <p class="text-sm text-gray-600 mt-1" id="zipStatus">Preparing ZIP archive...</p>
112
+ </div>
113
+ </div>
114
+ </div>
115
+
116
+ <div class="mt-6 text-center text-sm text-gray-500">
117
+ <p>Your files are processed locally in the browser — no data is uploaded anywhere.</p>
118
+ </div>
119
+ </div>
120
+
121
+ <script>
122
+ document.addEventListener('DOMContentLoaded', () => {
123
+ const fileInput = document.getElementById('fileInput');
124
+ const dropzone = document.getElementById('dropzone');
125
+ const filesList = document.getElementById('filesList');
126
+ const emptyState = document.getElementById('emptyState');
127
+ const renameBtn = document.getElementById('renameBtn');
128
+ const newExtension = document.getElementById('newExtension');
129
+ const downloadSection = document.getElementById('downloadSection');
130
+ const downloadAllBtn = document.getElementById('downloadAllBtn');
131
+ const downloadZipBtn = document.getElementById('downloadZipBtn');
132
+ const zipLoading = document.getElementById('zipLoading');
133
+ const zipProgress = document.getElementById('zipProgress');
134
+ const zipStatus = document.getElementById('zipStatus');
135
+ const progressBar = zipProgress.querySelector('.progress-bar');
136
+
137
+ let files = [];
138
+ let processedFiles = [];
139
+
140
+ // Drag and drop functionality
141
+ dropzone.addEventListener('dragover', (e) => {
142
+ e.preventDefault();
143
+ dropzone.classList.add('active');
144
+ });
145
+
146
+ dropzone.addEventListener('dragleave', () => {
147
+ dropzone.classList.remove('active');
148
+ });
149
+
150
+ dropzone.addEventListener('drop', (e) => {
151
+ e.preventDefault();
152
+ dropzone.classList.remove('active');
153
+ handleFiles(e.dataTransfer.files);
154
+ });
155
+
156
+ fileInput.addEventListener('change', (e) => {
157
+ handleFiles(e.target.files);
158
+ });
159
+
160
+ renameBtn.addEventListener('click', renameExtensions);
161
+ downloadAllBtn.addEventListener('click', downloadFiles);
162
+ downloadZipBtn.addEventListener('click', createZip);
163
+
164
+ function handleFiles(newFiles) {
165
+ if (newFiles.length === 0) return;
166
+
167
+ // Convert FileList to array and add to existing files
168
+ files = files.concat(Array.from(newFiles));
169
+
170
+ // Update UI
171
+ updateFilesList();
172
+ renameBtn.disabled = files.length === 0 || !newExtension.value.trim();
173
+
174
+ // Clear file input to allow selecting same files again
175
+ fileInput.value = '';
176
+ }
177
+
178
+ function updateFilesList() {
179
+ if (files.length === 0) {
180
+ emptyState.classList.remove('hidden');
181
+ filesList.innerHTML = '';
182
+ filesList.appendChild(emptyState);
183
+ return;
184
+ }
185
+
186
+ emptyState.classList.add('hidden');
187
+ filesList.innerHTML = '';
188
+
189
+ files.forEach((file, index) => {
190
+ const fileNameParts = file.name.split('.');
191
+ const ext = fileNameParts.length > 1 ? fileNameParts.pop() : '';
192
+ const nameWithoutExt = fileNameParts.join('.');
193
+
194
+ const fileItem = document.createElement('div');
195
+ fileItem.className = 'file-item flex items-center justify-between p-4 hover:bg-gray-50 transition fade-in';
196
+
197
+ fileItem.innerHTML = `
198
+ <div class="flex items-center min-w-0">
199
+ <div class="flex-shrink-0 h-10 w-10 rounded-full bg-indigo-50 flex items-center justify-center text-indigo-600 mr-4">
200
+ <i class="fas fa-file-alt"></i>
201
+ </div>
202
+ <div class="min-w-0">
203
+ <p class="text-sm font-medium text-gray-900 truncate">${nameWithoutExt || file.name}</p>
204
+ <p class="text-sm text-gray-500">${ext || 'no extension'}</p>
205
+ </div>
206
+ </div>
207
+ <div class="flex items-center ml-4">
208
+ <span class="text-xs font-mono bg-gray-100 px-2 py-1 rounded text-gray-700 mr-3">${formatFileSize(file.size)}</span>
209
+ <button class="remove-btn opacity-0 group focus:opacity-100 hover:opacity-100 transition" data-index="${index}">
210
+ <i class="fas fa-times text-gray-400 group-hover:text-red-500"></i>
211
+ </button>
212
+ </div>
213
+ `;
214
+
215
+ filesList.appendChild(fileItem);
216
+ });
217
+
218
+ // Add event listeners to remove buttons
219
+ document.querySelectorAll('.remove-btn').forEach(btn => {
220
+ btn.addEventListener('click', (e) => {
221
+ e.stopPropagation();
222
+ const index = parseInt(btn.getAttribute('data-index'));
223
+ files.splice(index, 1);
224
+ updateFilesList();
225
+ renameBtn.disabled = files.length === 0 || !newExtension.value.trim();
226
+ });
227
+ });
228
+ }
229
+
230
+ function renameExtensions() {
231
+ const ext = newExtension.value.trim();
232
+ if (!ext) return;
233
+
234
+ processedFiles = files.map(file => {
235
+ const fileNameParts = file.name.split('.');
236
+ const nameWithoutExt = fileNameParts.length > 1
237
+ ? fileNameParts.slice(0, -1).join('.')
238
+ : fileNameParts.join('.');
239
+
240
+ const newName = `${nameWithoutExt}.${ext}`;
241
+
242
+ // Create a new File object with the new name
243
+ return new File([file], newName, {
244
+ type: file.type,
245
+ lastModified: file.lastModified
246
+ });
247
+ });
248
+
249
+ // Show download section
250
+ downloadSection.classList.remove('hidden');
251
+
252
+ // Scroll to download button
253
+ downloadSection.scrollIntoView({ behavior: 'smooth' });
254
+ }
255
+
256
+ function downloadFiles() {
257
+ processedFiles.forEach(file => {
258
+ const url = URL.createObjectURL(file);
259
+ const a = document.createElement('a');
260
+ a.href = url;
261
+ a.download = file.name;
262
+ document.body.appendChild(a);
263
+ a.click();
264
+ document.body.removeChild(a);
265
+ URL.revokeObjectURL(url);
266
+ });
267
+ }
268
+
269
+ async function createZip() {
270
+ if (processedFiles.length === 0) return;
271
+
272
+ // Show loading state
273
+ downloadZipBtn.disabled = true;
274
+ zipLoading.style.display = 'block';
275
+ zipProgress.classList.remove('hidden');
276
+
277
+ const zip = new JSZip();
278
+ let processedCount = 0;
279
+
280
+ // Add files to ZIP with progress tracking
281
+ for (const file of processedFiles) {
282
+ try {
283
+ await new Promise((resolve) => {
284
+ const reader = new FileReader();
285
+ reader.onload = function(e) {
286
+ zip.file(file.name, e.target.result);
287
+ processedCount++;
288
+ updateProgress(processedCount);
289
+ resolve();
290
+ };
291
+ reader.readAsArrayBuffer(file);
292
+ });
293
+ } catch (error) {
294
+ console.error('Error adding file to ZIP:', error);
295
+ }
296
+ }
297
+
298
+ // Generate the ZIP file
299
+ try {
300
+ zipStatus.textContent = 'Compressing files...';
301
+
302
+ const content = await zip.generateAsync({
303
+ type: 'blob',
304
+ compression: 'DEFLATE',
305
+ compressionOptions: { level: 6 }
306
+ }, (metadata) => {
307
+ const progress = Math.round(metadata.percent);
308
+ progressBar.style.width = `${progress}%`;
309
+ zipStatus.textContent = `Compressing files... ${progress}%`;
310
+ });
311
+
312
+ // Download the ZIP
313
+ saveAs(content, 'renamed_files.zip');
314
+
315
+ // Reset UI
316
+ zipStatus.textContent = 'ZIP archive created successfully!';
317
+ progressBar.style.width = '100%';
318
+ progressBar.classList.add('bg-emerald-700');
319
+ } catch (error) {
320
+ console.error('Error generating ZIP:', error);
321
+ zipStatus.textContent = 'Error creating ZIP file';
322
+ progressBar.classList.add('bg-red-500');
323
+ } finally {
324
+ downloadZipBtn.disabled = false;
325
+ zipLoading.style.display = 'none';
326
+ }
327
+ }
328
+
329
+ function updateProgress(processedCount) {
330
+ const progress = Math.round((processedCount / processedFiles.length) * 100);
331
+ progressBar.style.width = `${progress}%`;
332
+ zipStatus.textContent = `Adding files to ZIP... ${processedCount}/${processedFiles.length}`;
333
+ }
334
+
335
+ function formatFileSize(bytes) {
336
+ if (bytes === 0) return '0 Bytes';
337
+ const k = 1024;
338
+ const sizes = ['Bytes', 'KB', 'MB', 'GB'];
339
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
340
+ return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
341
+ }
342
+
343
+ // Enable/disable rename button based on extension input
344
+ newExtension.addEventListener('input', () => {
345
+ renameBtn.disabled = files.length === 0 || !newExtension.value.trim();
346
+ });
347
+ });
348
+ </script>
349
+ <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=Ultronprime/extension-changer" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
350
+ </html>
prompts.txt ADDED
File without changes