beatbox1200 commited on
Commit
b2c4dd7
·
verified ·
1 Parent(s): c387f1e

error processing video, shared array buffer is not defined, please find out whats wrong with the video processing and fix it please. - Initial Deployment

Browse files
Files changed (2) hide show
  1. README.md +7 -5
  2. index.html +850 -19
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
- title: Vidcuttrim
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: vidcuttrim
3
+ emoji: 🐳
4
+ colorFrom: red
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,850 @@
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>Video Cutter & Trimmer | HuggingFace Space</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
+ <script src="https://cdn.jsdelivr.net/npm/@ffmpeg/ffmpeg@0.11.6/dist/ffmpeg.min.js"></script>
10
+ <script>
11
+ tailwind.config = {
12
+ theme: {
13
+ extend: {
14
+ colors: {
15
+ hf: {
16
+ purple: '#7b3ae1',
17
+ dark: '#0b0f19',
18
+ light: '#f5f5f7',
19
+ }
20
+ }
21
+ }
22
+ }
23
+ }
24
+ </script>
25
+ <style>
26
+ .video-container {
27
+ aspect-ratio: 16/9;
28
+ }
29
+ .timeline-handle {
30
+ width: 12px;
31
+ height: 20px;
32
+ top: -8px;
33
+ }
34
+ .timeline-handle::after {
35
+ content: '';
36
+ position: absolute;
37
+ width: 2px;
38
+ height: 10px;
39
+ background: white;
40
+ top: 5px;
41
+ left: 5px;
42
+ }
43
+ .progress-bar {
44
+ height: 4px;
45
+ }
46
+ @media (max-width: 768px) {
47
+ .controls-grid {
48
+ grid-template-columns: repeat(2, minmax(0, 1fr));
49
+ }
50
+ }
51
+ </style>
52
+ </head>
53
+ <body class="bg-hf-dark text-hf-light min-h-screen">
54
+ <div class="container mx-auto px-4 py-8 max-w-6xl">
55
+ <!-- Header -->
56
+ <header class="flex flex-col md:flex-row justify-between items-center mb-8 gap-4">
57
+ <div class="flex items-center gap-3">
58
+ <div class="bg-hf-purple p-2 rounded-lg">
59
+ <i class="fas fa-video text-white text-xl"></i>
60
+ </div>
61
+ <h1 class="text-2xl font-bold">Video Cutter & Trimmer</h1>
62
+ </div>
63
+ <div class="flex gap-2">
64
+ <button class="bg-hf-purple hover:bg-purple-800 px-4 py-2 rounded-lg flex items-center gap-2 transition">
65
+ <i class="fas fa-share"></i> Share
66
+ </button>
67
+ <button class="bg-gray-700 hover:bg-gray-600 px-4 py-2 rounded-lg flex items-center gap-2 transition">
68
+ <i class="fas fa-star"></i> Like
69
+ </button>
70
+ </div>
71
+ </header>
72
+
73
+ <!-- Main Content -->
74
+ <div class="grid grid-cols-1 lg:grid-cols-3 gap-8">
75
+ <!-- Left Panel - Upload & Settings -->
76
+ <div class="lg:col-span-1 bg-gray-800 rounded-xl p-6">
77
+ <h2 class="text-xl font-semibold mb-4 flex items-center gap-2">
78
+ <i class="fas fa-upload"></i> Upload Video
79
+ </h2>
80
+
81
+ <!-- File Upload -->
82
+ <div class="border-2 border-dashed border-gray-600 rounded-lg p-6 text-center mb-6 cursor-pointer hover:border-hf-purple transition"
83
+ id="dropZone">
84
+ <input type="file" id="fileInput" class="hidden" accept=".mp4,.avi,.mkv,.mov">
85
+ <div class="flex flex-col items-center justify-center">
86
+ <i class="fas fa-cloud-upload-alt text-4xl text-hf-purple mb-3"></i>
87
+ <p class="mb-2">Drag & drop your video file here</p>
88
+ <p class="text-sm text-gray-400 mb-3">or</p>
89
+ <button class="bg-hf-purple hover:bg-purple-800 px-4 py-2 rounded-lg transition">
90
+ Browse Files
91
+ </button>
92
+ </div>
93
+ <p class="text-xs text-gray-400 mt-3">Supports: MP4, AVI, MKV, MOV</p>
94
+ </div>
95
+
96
+ <!-- Video Info -->
97
+ <div id="videoInfo" class="hidden mb-6">
98
+ <div class="flex justify-between items-center mb-2">
99
+ <h3 class="font-medium">File Info</h3>
100
+ <button id="clearFile" class="text-red-400 hover:text-red-300 text-sm">
101
+ <i class="fas fa-times"></i> Clear
102
+ </button>
103
+ </div>
104
+ <div class="bg-gray-700 rounded-lg p-3">
105
+ <div class="flex justify-between mb-1">
106
+ <span class="text-gray-400">Name:</span>
107
+ <span id="fileName" class="font-mono">video.mp4</span>
108
+ </div>
109
+ <div class="flex justify-between mb-1">
110
+ <span class="text-gray-400">Size:</span>
111
+ <span id="fileSize" class="font-mono">24.5 MB</span>
112
+ </div>
113
+ <div class="flex justify-between mb-1">
114
+ <span class="text-gray-400">Duration:</span>
115
+ <span id="fileDuration" class="font-mono">02:45</span>
116
+ </div>
117
+ <div class="flex justify-between">
118
+ <span class="text-gray-400">Resolution:</span>
119
+ <span id="fileResolution" class="font-mono">1920x1080</span>
120
+ </div>
121
+ </div>
122
+ </div>
123
+
124
+ <!-- Output Settings -->
125
+ <div class="mb-6">
126
+ <h3 class="font-medium mb-3 flex items-center gap-2">
127
+ <i class="fas fa-cog"></i> Output Settings
128
+ </h3>
129
+ <div class="space-y-4">
130
+ <div>
131
+ <label class="block text-sm text-gray-400 mb-1">Format</label>
132
+ <select class="w-full bg-gray-700 border border-gray-600 rounded-lg px-3 py-2">
133
+ <option value="mp4">MP4 (Default)</option>
134
+ <option value="avi">AVI</option>
135
+ <option value="mkv">MKV</option>
136
+ <option value="mov">MOV</option>
137
+ </select>
138
+ </div>
139
+ <div>
140
+ <label class="block text-sm text-gray-400 mb-1">Quality</label>
141
+ <select class="w-full bg-gray-700 border border-gray-600 rounded-lg px-3 py-2">
142
+ <option value="original">Original (No compression)</option>
143
+ <option value="high">High (90% quality)</option>
144
+ <option value="medium">Medium (75% quality)</option>
145
+ <option value="low">Low (50% quality)</option>
146
+ </select>
147
+ </div>
148
+ <div class="flex items-center gap-2">
149
+ <input type="checkbox" id="preserveAudio" checked class="rounded bg-gray-700">
150
+ <label for="preserveAudio" class="text-sm">Preserve original audio quality</label>
151
+ </div>
152
+ </div>
153
+ </div>
154
+
155
+ <!-- Action Buttons -->
156
+ <div class="space-y-3">
157
+ <button id="processBtn" disabled class="w-full bg-hf-purple hover:bg-purple-800 py-3 rounded-lg font-medium flex items-center justify-center gap-2 transition opacity-50">
158
+ <i class="fas fa-cut"></i> Process Video
159
+ </button>
160
+ <button id="downloadBtn" disabled class="w-full bg-gray-700 hover:bg-gray-600 py-3 rounded-lg font-medium flex items-center justify-center gap-2 transition opacity-50">
161
+ <i class="fas fa-download"></i> Download
162
+ </button>
163
+ </div>
164
+ </div>
165
+
166
+ <!-- Right Panel - Video Preview & Editor -->
167
+ <div class="lg:col-span-2 space-y-6">
168
+ <!-- Video Preview -->
169
+ <div class="bg-gray-800 rounded-xl p-6">
170
+ <h2 class="text-xl font-semibold mb-4 flex items-center gap-2">
171
+ <i class="fas fa-play"></i> Preview
172
+ </h2>
173
+ <div class="video-container bg-black rounded-lg overflow-hidden relative" id="videoContainer">
174
+ <video id="videoPlayer" class="w-full h-full object-contain" controls></video>
175
+ <div id="noVideo" class="absolute inset-0 flex items-center justify-center">
176
+ <div class="text-center p-6">
177
+ <i class="fas fa-video-slash text-4xl text-gray-600 mb-3"></i>
178
+ <p class="text-gray-400">No video loaded</p>
179
+ </div>
180
+ </div>
181
+ </div>
182
+ </div>
183
+
184
+ <!-- Timeline Editor -->
185
+ <div class="bg-gray-800 rounded-xl p-6">
186
+ <h2 class="text-xl font-semibold mb-4 flex items-center gap-2">
187
+ <i class="fas fa-sliders-h"></i> Editor
188
+ </h2>
189
+
190
+ <!-- Trim Controls -->
191
+ <div class="mb-6">
192
+ <h3 class="font-medium mb-3">Trim Video</h3>
193
+ <div class="grid grid-cols-2 gap-4 mb-4">
194
+ <div>
195
+ <label class="block text-sm text-gray-400 mb-1">Start Time</label>
196
+ <div class="flex gap-2">
197
+ <input type="number" min="0" value="0" class="w-full bg-gray-700 border border-gray-600 rounded-lg px-3 py-2" id="startMin">
198
+ <span class="flex items-center">:</span>
199
+ <input type="number" min="0" max="59" value="0" class="w-full bg-gray-700 border border-gray-600 rounded-lg px-3 py-2" id="startSec">
200
+ </div>
201
+ </div>
202
+ <div>
203
+ <label class="block text-sm text-gray-400 mb-1">End Time</label>
204
+ <div class="flex gap-2">
205
+ <input type="number" min="0" value="0" class="w-full bg-gray-700 border border-gray-600 rounded-lg px-3 py-2" id="endMin">
206
+ <span class="flex items-center">:</span>
207
+ <input type="number" min="0" max="59" value="0" class="w-full bg-gray-700 border border-gray-600 rounded-lg px-3 py-2" id="endSec">
208
+ </div>
209
+ </div>
210
+ </div>
211
+ </div>
212
+
213
+ <!-- Timeline Visualization -->
214
+ <div class="mb-6">
215
+ <div class="flex justify-between mb-2">
216
+ <span id="currentTime">00:00</span>
217
+ <span id="totalDuration">00:00</span>
218
+ </div>
219
+ <div class="relative h-2 bg-gray-700 rounded-full overflow-hidden mb-2">
220
+ <div class="absolute top-0 left-0 h-full bg-hf-purple" id="progressBar"></div>
221
+ </div>
222
+ <div class="relative h-8 bg-gray-700 rounded-lg overflow-hidden">
223
+ <div class="absolute inset-0 flex items-center">
224
+ <div class="h-4 w-full bg-gray-600 rounded" id="timelineBg"></div>
225
+ </div>
226
+ <div class="absolute inset-0 flex items-center">
227
+ <div class="h-4 bg-hf-purple rounded" id="timelineSelection"></div>
228
+ </div>
229
+ <div class="absolute timeline-handle bg-hf-purple rounded cursor-move" id="startHandle"></div>
230
+ <div class="absolute timeline-handle bg-hf-purple rounded cursor-move" id="endHandle"></div>
231
+ </div>
232
+ </div>
233
+
234
+ <!-- Crop Controls -->
235
+ <div class="mb-6">
236
+ <h3 class="font-medium mb-3">Crop Video</h3>
237
+ <div class="flex flex-wrap gap-3 mb-4">
238
+ <button class="bg-gray-700 hover:bg-gray-600 px-4 py-2 rounded-lg flex items-center gap-2 transition crop-btn" data-ratio="original">
239
+ <i class="fas fa-expand"></i> Original
240
+ </button>
241
+ <button class="bg-gray-700 hover:bg-gray-600 px-4 py-2 rounded-lg flex items-center gap-2 transition crop-btn" data-ratio="16:9">
242
+ <i class="fas fa-expand"></i> 16:9
243
+ </button>
244
+ <button class="bg-gray-700 hover:bg-gray-600 px-4 py-2 rounded-lg flex items-center gap-2 transition crop-btn" data-ratio="4:3">
245
+ <i class="fas fa-expand"></i> 4:3
246
+ </button>
247
+ <button class="bg-gray-700 hover:bg-gray-600 px-4 py-2 rounded-lg flex items-center gap-2 transition crop-btn" data-ratio="1:1">
248
+ <i class="fas fa-expand"></i> Square (1:1)
249
+ </button>
250
+ </div>
251
+ <div class="hidden" id="cropOverlayContainer">
252
+ <div class="relative border-2 border-hf-purple rounded-lg overflow-hidden" id="cropOverlay"></div>
253
+ <div class="flex justify-between mt-2">
254
+ <span class="text-sm text-gray-400">Drag edges to adjust crop</span>
255
+ <button id="applyCrop" class="text-sm text-hf-purple hover:text-purple-300">Apply Crop</button>
256
+ </div>
257
+ </div>
258
+ </div>
259
+
260
+ <!-- Trim Action Buttons -->
261
+ <div class="flex flex-wrap gap-3 mt-6">
262
+ <button id="setStartBtn" class="bg-hf-purple hover:bg-purple-800 px-4 py-2 rounded-lg flex items-center gap-2 transition">
263
+ <i class="fas fa-flag"></i> Set Start Time Here
264
+ </button>
265
+ <button id="setEndBtn" class="bg-hf-purple hover:bg-purple-800 px-4 py-2 rounded-lg flex items-center gap-2 transition">
266
+ <i class="fas fa-flag-checkered"></i> Set End Time Here
267
+ </button>
268
+ </div>
269
+ </div>
270
+
271
+ <!-- Output Preview (hidden by default) -->
272
+ <div id="outputPreview" class="bg-gray-800 rounded-xl p-6 hidden">
273
+ <div class="flex justify-between items-center mb-4">
274
+ <h2 class="text-xl font-semibold flex items-center gap-2">
275
+ <i class="fas fa-check-circle"></i> Processed Video Preview
276
+ </h2>
277
+ <div class="flex gap-2">
278
+ <button id="backToEditBtn" class="bg-gray-700 hover:bg-gray-600 px-4 py-2 rounded-lg flex items-center gap-2 transition">
279
+ <i class="fas fa-edit"></i> Back to Editing
280
+ </button>
281
+ <button id="startOverBtn" class="bg-gray-700 hover:bg-gray-600 px-4 py-2 rounded-lg flex items-center gap-2 transition">
282
+ <i class="fas fa-redo"></i> Start Over
283
+ </button>
284
+ </div>
285
+ </div>
286
+
287
+ <div class="mb-4 bg-gray-700 rounded-lg p-3">
288
+ <div class="flex justify-between mb-1">
289
+ <span class="text-gray-400">Trimmed Duration:</span>
290
+ <span id="processedDuration" class="font-mono">00:00</span>
291
+ </div>
292
+ <div class="flex justify-between">
293
+ <span class="text-gray-400">Output Resolution:</span>
294
+ <span id="processedResolution" class="font-mono">1920x1080</span>
295
+ </div>
296
+ </div>
297
+
298
+ <div class="video-container bg-black rounded-lg overflow-hidden mb-4">
299
+ <video id="outputVideo" class="w-full h-full" controls></video>
300
+ </div>
301
+
302
+ <div class="flex flex-col sm:flex-row gap-3">
303
+ <button id="finalDownloadBtn" disabled class="flex-1 bg-hf-purple hover:bg-purple-800 py-3 rounded-lg font-medium flex items-center justify-center gap-2 transition opacity-50">
304
+ <i class="fas fa-download"></i> Download Now
305
+ </button>
306
+ <button id="startOverBtn" class="flex-1 bg-gray-700 hover:bg-gray-600 py-3 rounded-lg font-medium flex items-center justify-center gap-2 transition">
307
+ <i class="fas fa-redo"></i> Process Again
308
+ </button>
309
+ </div>
310
+ </div>
311
+ </div>
312
+ </div>
313
+
314
+ <!-- Footer -->
315
+ <footer class="mt-12 text-center text-gray-400 text-sm">
316
+ <p>Video Cutter & Trimmer - A HuggingFace Space Project</p>
317
+ <p class="mt-1">Supports MP4, AVI, MKV, MOV formats with original quality preservation</p>
318
+ </footer>
319
+ </div>
320
+
321
+ <script>
322
+ // DOM Elements
323
+ const fileInput = document.getElementById('fileInput');
324
+ const dropZone = document.getElementById('dropZone');
325
+ const videoPlayer = document.getElementById('videoPlayer');
326
+ const noVideo = document.getElementById('noVideo');
327
+ const videoContainer = document.getElementById('videoContainer');
328
+ const videoInfo = document.getElementById('videoInfo');
329
+ const fileName = document.getElementById('fileName');
330
+ const fileSize = document.getElementById('fileSize');
331
+ const fileDuration = document.getElementById('fileDuration');
332
+ const fileResolution = document.getElementById('fileResolution');
333
+ const clearFile = document.getElementById('clearFile');
334
+ const processBtn = document.getElementById('processBtn');
335
+ const downloadBtn = document.getElementById('downloadBtn');
336
+ const progressBar = document.getElementById('progressBar');
337
+ const timelineBg = document.getElementById('timelineBg');
338
+ const timelineSelection = document.getElementById('timelineSelection');
339
+ const startHandle = document.getElementById('startHandle');
340
+ const endHandle = document.getElementById('endHandle');
341
+ const currentTime = document.getElementById('currentTime');
342
+ const totalDuration = document.getElementById('totalDuration');
343
+ const startMin = document.getElementById('startMin');
344
+ const startSec = document.getElementById('startSec');
345
+ const endMin = document.getElementById('endMin');
346
+ const endSec = document.getElementById('endSec');
347
+
348
+ // Variables
349
+ let videoFile = null;
350
+ let isDragging = false;
351
+ let activeHandle = null;
352
+ let videoDuration = 0;
353
+ let currentCropRatio = 'original';
354
+ let cropOverlay = null;
355
+ let isCropping = false;
356
+
357
+ // Event Listeners
358
+ dropZone.addEventListener('click', () => fileInput.click());
359
+ fileInput.addEventListener('change', handleFileSelect);
360
+ dropZone.addEventListener('dragover', handleDragOver);
361
+ dropZone.addEventListener('dragleave', handleDragLeave);
362
+ dropZone.addEventListener('drop', handleDrop);
363
+ clearFile.addEventListener('click', clearVideo);
364
+ videoPlayer.addEventListener('timeupdate', updateProgress);
365
+ videoPlayer.addEventListener('loadedmetadata', setupVideo);
366
+ startHandle.addEventListener('mousedown', startDrag);
367
+ endHandle.addEventListener('mousedown', startDrag);
368
+ document.addEventListener('mousemove', handleDrag);
369
+ document.addEventListener('mouseup', stopDrag);
370
+ startMin.addEventListener('change', updateFromTimeInputs);
371
+ startSec.addEventListener('change', updateFromTimeInputs);
372
+ endMin.addEventListener('change', updateFromTimeInputs);
373
+ endSec.addEventListener('change', updateFromTimeInputs);
374
+
375
+ // New DOM elements
376
+ const setStartBtn = document.getElementById('setStartBtn');
377
+ const setEndBtn = document.getElementById('setEndBtn');
378
+ const outputPreview = document.getElementById('outputPreview');
379
+ const outputVideo = document.getElementById('outputVideo');
380
+ const backToEditBtn = document.getElementById('backToEditBtn');
381
+ const finalDownloadBtn = document.getElementById('finalDownloadBtn');
382
+ const startOverBtn = document.getElementById('startOverBtn');
383
+
384
+ // Event listeners
385
+ setStartBtn.addEventListener('click', () => setTrimPoint('start'));
386
+ setEndBtn.addEventListener('click', () => setTrimPoint('end'));
387
+ backToEditBtn.addEventListener('click', () => {
388
+ outputPreview.classList.add('hidden');
389
+ });
390
+ startOverBtn.addEventListener('click', clearVideo);
391
+ finalDownloadBtn.addEventListener('click', downloadProcessedVideo);
392
+ processBtn.addEventListener('click', processVideo);
393
+
394
+ // Crop event listeners
395
+ document.querySelectorAll('.crop-btn').forEach(btn => {
396
+ btn.addEventListener('click', handleCropSelection);
397
+ });
398
+
399
+ // Functions
400
+ function handleFileSelect(e) {
401
+ const file = e.target.files[0];
402
+ if (file && isValidFile(file)) {
403
+ loadVideo(file);
404
+ }
405
+ }
406
+
407
+ function handleDragOver(e) {
408
+ e.preventDefault();
409
+ dropZone.classList.add('border-hf-purple');
410
+ }
411
+
412
+ function handleDragLeave() {
413
+ dropZone.classList.remove('border-hf-purple');
414
+ }
415
+
416
+ function handleDrop(e) {
417
+ e.preventDefault();
418
+ dropZone.classList.remove('border-hf-purple');
419
+
420
+ const file = e.dataTransfer.files[0];
421
+ if (file && isValidFile(file)) {
422
+ loadVideo(file);
423
+ }
424
+ }
425
+
426
+ function isValidFile(file) {
427
+ const validTypes = ['video/mp4', 'video/quicktime', 'video/x-msvideo', 'video/x-matroska'];
428
+ const validExtensions = ['.mp4', '.avi', '.mkv', '.mov'];
429
+ const extension = file.name.substring(file.name.lastIndexOf('.')).toLowerCase();
430
+
431
+ return validTypes.includes(file.type) || validExtensions.includes(extension);
432
+ }
433
+
434
+ function loadVideo(file) {
435
+ videoFile = file;
436
+ const url = URL.createObjectURL(file);
437
+ videoPlayer.src = url;
438
+
439
+ // Show video info
440
+ fileName.textContent = file.name;
441
+ fileSize.textContent = formatFileSize(file.size);
442
+
443
+ // Show video elements
444
+ noVideo.classList.add('hidden');
445
+ videoInfo.classList.remove('hidden');
446
+ processBtn.disabled = false;
447
+ processBtn.classList.remove('opacity-50');
448
+
449
+ // Wait for metadata to be loaded
450
+ if (videoPlayer.readyState > 0) {
451
+ setupVideo();
452
+ }
453
+ }
454
+
455
+ function setupVideo() {
456
+ videoDuration = videoPlayer.duration;
457
+ fileDuration.textContent = formatTime(videoDuration);
458
+ totalDuration.textContent = formatTime(videoDuration);
459
+
460
+ // Set end time inputs
461
+ const endMinutes = Math.floor(videoDuration / 60);
462
+ const endSeconds = Math.floor(videoDuration % 60);
463
+ endMin.value = endMinutes;
464
+ endSec.value = endSeconds;
465
+
466
+ // Setup timeline
467
+ setupTimeline();
468
+ }
469
+
470
+ function setupTimeline() {
471
+ timelineBg.style.width = '100%';
472
+
473
+ // Position handles
474
+ startHandle.style.left = '0%';
475
+ endHandle.style.left = '100%';
476
+
477
+ // Initial selection
478
+ updateSelection();
479
+ }
480
+
481
+ function updateSelection() {
482
+ const startPos = parseFloat(startHandle.style.left) / 100;
483
+ const endPos = parseFloat(endHandle.style.left) / 100;
484
+
485
+ timelineSelection.style.left = `${startPos * 100}%`;
486
+ timelineSelection.style.width = `${(endPos - startPos) * 100}%`;
487
+
488
+ // Update time inputs
489
+ const startTime = startPos * videoDuration;
490
+ const endTime = endPos * videoDuration;
491
+
492
+ const startMinutes = Math.floor(startTime / 60);
493
+ const startSeconds = Math.floor(startTime % 60);
494
+ startMin.value = startMinutes;
495
+ startSec.value = startSeconds;
496
+
497
+ const endMinutes = Math.floor(endTime / 60);
498
+ const endSeconds = Math.floor(endTime % 60);
499
+ endMin.value = endMinutes;
500
+ endSec.value = endSeconds;
501
+ }
502
+
503
+ function updateFromTimeInputs() {
504
+ const startTime = (parseInt(startMin.value) || 0) * 60 + (parseInt(startSec.value) || 0);
505
+ const endTime = (parseInt(endMin.value) || 0) * 60 + (parseInt(endSec.value) || 0);
506
+
507
+ // Validate times
508
+ if (startTime < 0) startTime = 0;
509
+ if (endTime > videoDuration) endTime = videoDuration;
510
+ if (startTime > endTime) startTime = endTime;
511
+
512
+ // Update handles
513
+ startHandle.style.left = `${(startTime / videoDuration) * 100}%`;
514
+ endHandle.style.left = `${(endTime / videoDuration) * 100}%`;
515
+
516
+ updateSelection();
517
+
518
+ // Seek video if playing
519
+ if (!videoPlayer.paused) {
520
+ videoPlayer.currentTime = startTime;
521
+ }
522
+ }
523
+
524
+ function startDrag(e) {
525
+ isDragging = true;
526
+ activeHandle = e.target;
527
+ e.preventDefault();
528
+ }
529
+
530
+ function handleDrag(e) {
531
+ if (!isDragging) return;
532
+
533
+ const rect = timelineBg.getBoundingClientRect();
534
+ let pos = (e.clientX - rect.left) / rect.width;
535
+
536
+ // Constrain position
537
+ pos = Math.max(0, Math.min(1, pos));
538
+
539
+ if (activeHandle === startHandle) {
540
+ const endPos = parseFloat(endHandle.style.left) / 100;
541
+ pos = Math.min(pos, endPos - 0.01); // Small offset to prevent overlap
542
+ activeHandle.style.left = `${pos * 100}%`;
543
+ } else if (activeHandle === endHandle) {
544
+ const startPos = parseFloat(startHandle.style.left) / 100;
545
+ pos = Math.max(pos, startPos + 0.01); // Small offset to prevent overlap
546
+ activeHandle.style.left = `${pos * 100}%`;
547
+ }
548
+
549
+ updateSelection();
550
+ }
551
+
552
+ function stopDrag() {
553
+ isDragging = false;
554
+ activeHandle = null;
555
+ }
556
+
557
+ function setTrimPoint(type) {
558
+ const currentTime = videoPlayer.currentTime;
559
+ const percentage = (currentTime / videoDuration) * 100;
560
+
561
+ if (type === 'start') {
562
+ startHandle.style.left = `${percentage}%`;
563
+ startMin.value = Math.floor(currentTime / 60);
564
+ startSec.value = Math.floor(currentTime % 60);
565
+ } else {
566
+ endHandle.style.left = `${percentage}%`;
567
+ endMin.value = Math.floor(currentTime / 60);
568
+ endSec.value = Math.floor(currentTime % 60);
569
+ }
570
+
571
+ updateSelection();
572
+ }
573
+
574
+ // Connect process button
575
+ processBtn.addEventListener('click', processVideo);
576
+
577
+ // Processing state variables
578
+ let processingInterval;
579
+ let processingProgress = 0;
580
+ const processingMessages = [
581
+ "Preparing video for processing...",
582
+ "Analyzing video metadata...",
583
+ "Trimming video segment (${startTime}s to ${endTime}s)...",
584
+ "Applying crop settings (${currentCropRatio})...",
585
+ "Encoding video output...",
586
+ "Finalizing processed video..."
587
+ ];
588
+
589
+ async function processVideo() {
590
+ if (!videoFile) {
591
+ alert('Please upload a video first');
592
+ return;
593
+ }
594
+
595
+ // Check for browser support
596
+ if (typeof SharedArrayBuffer === 'undefined') {
597
+ alert('Your browser doesn\'t support required features for video processing. Please try a modern browser like Chrome or Firefox.');
598
+ return;
599
+ }
600
+
601
+ // Show processing overlay
602
+ const processingOverlay = document.createElement('div');
603
+ processingOverlay.className = 'fixed inset-0 bg-black bg-opacity-75 flex flex-col items-center justify-center z-50';
604
+ processingOverlay.innerHTML = `
605
+ <div class="bg-gray-800 rounded-xl p-8 max-w-md w-full">
606
+ <h3 class="text-xl font-semibold mb-4">Processing Video</h3>
607
+ <div class="mb-2 text-sm" id="processingMessage">Initializing FFmpeg...</div>
608
+ <div class="w-full bg-gray-700 rounded-full h-2.5 mb-4">
609
+ <div id="processingBar" class="bg-hf-purple h-2.5 rounded-full" style="width: 0%"></div>
610
+ </div>
611
+ <div class="flex justify-between text-xs text-gray-400">
612
+ <span id="processingPercent">0%</span>
613
+ <span id="processingTime">Estimated time: calculating...</span>
614
+ </div>
615
+ </div>
616
+ `;
617
+ document.body.appendChild(processingOverlay);
618
+
619
+ try {
620
+ const { createFFmpeg, fetchFile } = FFmpeg;
621
+ const ffmpeg = createFFmpeg({
622
+ log: true,
623
+ corePath: 'https://unpkg.com/@ffmpeg/core@0.11.0/dist/ffmpeg-core.js',
624
+ progress: ({ ratio }) => {
625
+ const percent = Math.round(ratio * 100);
626
+ document.getElementById('processingBar').style.width = `${percent}%`;
627
+ document.getElementById('processingPercent').textContent = `${percent}%`;
628
+ document.getElementById('processingMessage').textContent =
629
+ percent < 30 ? "Loading FFmpeg..." :
630
+ percent < 60 ? "Processing video..." :
631
+ "Finalizing output...";
632
+ }
633
+ });
634
+
635
+ try {
636
+ await ffmpeg.load();
637
+ } catch (error) {
638
+ console.error('FFmpeg load error:', error);
639
+ processingOverlay.remove();
640
+ alert('Error: Your browser doesn\'t support required features for video processing. Please try a modern browser like Chrome or Firefox.');
641
+ return;
642
+ }
643
+
644
+ // Get trim times
645
+ const startTime = (parseInt(startMin.value) || 0) * 60 + (parseInt(startSec.value) || 0);
646
+ const endTime = (parseInt(endMin.value) || 0) * 60 + (parseInt(endSec.value) || 0);
647
+ const duration = endTime - startTime;
648
+
649
+ // Read input file
650
+ ffmpeg.FS('writeFile', 'input.mp4', await fetchFile(videoFile));
651
+
652
+ // Build FFmpeg command
653
+ let command = [
654
+ '-i', 'input.mp4',
655
+ '-ss', startTime.toString(),
656
+ '-t', duration.toString()
657
+ ];
658
+
659
+ // Add crop if needed
660
+ if (currentCropRatio !== 'original') {
661
+ const [w, h] = currentCropRatio.split(':').map(Number);
662
+ command.push('-vf', `crop=in_w*${w}/${w+1}:in_h*${h}/${h+1}`);
663
+ }
664
+
665
+ command.push('-c:v', 'libx264', '-preset', 'fast', '-c:a', 'copy', 'output.mp4');
666
+
667
+ try {
668
+ // Run FFmpeg
669
+ await ffmpeg.run(...command);
670
+
671
+ // Get output
672
+ const data = ffmpeg.FS('readFile', 'output.mp4');
673
+ const blob = new Blob([data.buffer], { type: 'video/mp4' });
674
+ processedVideoBlob = blob;
675
+ const url = URL.createObjectURL(blob);
676
+
677
+ // Show processed video
678
+ processingOverlay.remove();
679
+ showProcessedVideo(url, duration);
680
+ } catch (error) {
681
+ console.error('FFmpeg processing error:', error);
682
+ processingOverlay.remove();
683
+ alert('Error processing video. Please try a different video or browser.');
684
+ return;
685
+ }
686
+ } catch (error) {
687
+ console.error('Error processing video:', error);
688
+ processingOverlay.remove();
689
+ alert('Error processing video: ' + error.message);
690
+ }
691
+ }
692
+
693
+ let processedVideoBlob = null;
694
+
695
+ function showProcessedVideo(url, duration) {
696
+ outputPreview.classList.remove('hidden');
697
+
698
+ // Create preview with actual processed video
699
+ const previewContainer = document.getElementById('outputVideo').parentNode;
700
+ previewContainer.innerHTML = `
701
+ <div class="relative w-full h-full">
702
+ <video id="outputVideo" class="w-full h-full" controls playsinline>
703
+ <source src="${url}" type="video/mp4">
704
+ </video>
705
+ </div>
706
+ `;
707
+
708
+ // Show trimmed duration
709
+ document.getElementById('processedDuration').textContent = formatTime(duration);
710
+
711
+ // Enable download button
712
+ finalDownloadBtn.disabled = false;
713
+ finalDownloadBtn.classList.remove('opacity-50');
714
+
715
+ // Scroll to output preview
716
+ outputPreview.scrollIntoView({ behavior: 'smooth' });
717
+ }
718
+
719
+ function downloadProcessedVideo() {
720
+ if (!processedVideoBlob) return;
721
+
722
+ const url = URL.createObjectURL(processedVideoBlob);
723
+ const a = document.createElement('a');
724
+ a.href = url;
725
+ a.download = `edited_${videoFile.name}`;
726
+ document.body.appendChild(a);
727
+ a.click();
728
+ setTimeout(() => {
729
+ document.body.removeChild(a);
730
+ URL.revokeObjectURL(url);
731
+ }, 100);
732
+
733
+ // Show download complete message
734
+ const toast = document.createElement('div');
735
+ toast.className = 'fixed bottom-4 right-4 bg-green-600 text-white px-4 py-2 rounded-lg shadow-lg';
736
+ toast.textContent = 'Download started!';
737
+ document.body.appendChild(toast);
738
+ setTimeout(() => toast.remove(), 3000);
739
+ }
740
+
741
+ function updateProgress() {
742
+ const currentTimeValue = videoPlayer.currentTime;
743
+ const duration = videoPlayer.duration;
744
+
745
+ // Update progress bar
746
+ progressBar.style.width = `${(currentTimeValue / duration) * 100}%`;
747
+
748
+ // Update current time display
749
+ currentTime.textContent = formatTime(currentTimeValue);
750
+
751
+ // Check if current time is outside selection
752
+ const startPos = parseFloat(startHandle.style.left) / 100;
753
+ const endPos = parseFloat(endHandle.style.left) / 100;
754
+
755
+ if (currentTimeValue < startPos * duration || currentTimeValue > endPos * duration) {
756
+ videoPlayer.pause();
757
+ videoPlayer.currentTime = startPos * duration;
758
+ }
759
+ }
760
+
761
+ function clearVideo() {
762
+ videoPlayer.src = '';
763
+ videoFile = null;
764
+ noVideo.classList.remove('hidden');
765
+ videoInfo.classList.add('hidden');
766
+ processBtn.disabled = true;
767
+ processBtn.classList.add('opacity-50');
768
+ downloadBtn.disabled = true;
769
+ downloadBtn.classList.add('opacity-50');
770
+ fileInput.value = '';
771
+
772
+ // Reset timeline
773
+ progressBar.style.width = '0%';
774
+ currentTime.textContent = '00:00';
775
+ totalDuration.textContent = '00:00';
776
+ timelineSelection.style.width = '0%';
777
+ }
778
+
779
+ function formatTime(seconds) {
780
+ const mins = Math.floor(seconds / 60);
781
+ const secs = Math.floor(seconds % 60);
782
+ return `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
783
+ }
784
+
785
+ function formatFileSize(bytes) {
786
+ if (bytes === 0) return '0 Bytes';
787
+ const k = 1024;
788
+ const sizes = ['Bytes', 'KB', 'MB', 'GB'];
789
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
790
+ return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
791
+ }
792
+
793
+ function handleCropSelection(e) {
794
+ const ratio = e.target.dataset.ratio;
795
+ currentCropRatio = ratio;
796
+
797
+ // Highlight selected button
798
+ document.querySelectorAll('.crop-btn').forEach(btn => {
799
+ btn.classList.remove('bg-hf-purple');
800
+ btn.classList.add('bg-gray-700');
801
+ });
802
+ e.target.classList.remove('bg-gray-700');
803
+ e.target.classList.add('bg-hf-purple');
804
+
805
+ if (ratio === 'original') {
806
+ document.getElementById('cropOverlayContainer').classList.add('hidden');
807
+ videoPlayer.style.objectFit = 'contain';
808
+ return;
809
+ }
810
+
811
+ // Show crop overlay
812
+ document.getElementById('cropOverlayContainer').classList.remove('hidden');
813
+ const container = document.getElementById('cropOverlay');
814
+ container.innerHTML = '';
815
+
816
+ // Create crop overlay based on ratio
817
+ const [w, h] = ratio.split(':').map(Number);
818
+ const aspectRatio = w / h;
819
+
820
+ // Create full-size overlay
821
+ cropOverlay = document.createElement('div');
822
+ cropOverlay.className = 'absolute inset-0 bg-black bg-opacity-50';
823
+
824
+ // Calculate and apply the crop mask
825
+ const containerWidth = container.offsetWidth;
826
+ const containerHeight = container.offsetHeight;
827
+ const containerAspect = containerWidth / containerHeight;
828
+
829
+ if (containerAspect > aspectRatio) {
830
+ // Container is wider than target aspect - crop sides
831
+ const newWidth = containerHeight * aspectRatio;
832
+ const sideMargin = (containerWidth - newWidth) / 2;
833
+ cropOverlay.style.left = `${sideMargin}px`;
834
+ cropOverlay.style.right = `${sideMargin}px`;
835
+ } else {
836
+ // Container is taller than target aspect - crop top/bottom
837
+ const newHeight = containerWidth / aspectRatio;
838
+ const vertMargin = (containerHeight - newHeight) / 2;
839
+ cropOverlay.style.top = `${vertMargin}px`;
840
+ cropOverlay.style.bottom = `${vertMargin}px`;
841
+ }
842
+
843
+ container.appendChild(cropOverlay);
844
+
845
+ // Store crop settings for processing
846
+ console.log('Auto-crop applied with ratio:', ratio);
847
+ }
848
+ </script>
849
+ <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=beatbox1200/vidcuttrim" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
850
+ </html>