coollsd commited on
Commit
c0ee329
1 Parent(s): 910736e

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +0 -585
app.py CHANGED
@@ -6,486 +6,7 @@ import asyncio
6
  from typing import Dict
7
  import os
8
  import shutil
9
-
10
  app = FastAPI()
11
-
12
- HTML_CONTENT = """
13
- <!DOCTYPE html>
14
- <html lang="en">
15
- <head>
16
- <!-- (HTML content remains the same as before) -->
17
- <!-- ... [omitted for brevity, use the same HTML_CONTENT as before] ... -->
18
-
19
- <style>
20
- /* (Styles remain the same as before) /
21
- </style>
22
- </head>
23
- <body>
24
- <!-- (Body content remains the same as before) -->
25
- <!-- ... [omitted for brevity, use the same HTML_CONTENT as before] ... -->
26
-
27
- <script>
28
- const fileInput = document.getElementById('file');
29
- const fileName = document.getElementById('fileName');
30
- const uploadForm = document.getElementById('uploadForm');
31
- const progressContainer = document.getElementById('progressContainer');
32
- const loadingSpinner = document.getElementById('loadingSpinner');
33
- const resultContainer = document.getElementById('resultContainer');
34
- const dropZone = document.getElementById('dropZone');
35
- const modal = document.getElementById('embedModal');
36
- const span = document.getElementsByClassName("close")[0];
37
- const embedLinkInput = document.getElementById('embedLink');
38
- const uploadBtn = document.getElementById('uploadBtn');
39
-
40
- fileInput.addEventListener('change', handleFileSelect);
41
-
42
- uploadForm.addEventListener('submit', (e) => {
43
- e.preventDefault();
44
- if (fileInput.files.length > 0) {
45
- uploadFile(fileInput.files[0]);
46
- }
47
- });
48
-
49
- dropZone.addEventListener('dragover', (e) => {
50
- e.preventDefault();
51
- dropZone.classList.add('drag-over');
52
- });
53
-
54
- dropZone.addEventListener('dragleave', () => {
55
- dropZone.classList.remove('drag-over');
56
- });
57
-
58
- dropZone.addEventListener('drop', (e) => {
59
- e.preventDefault();
60
- dropZone.classList.remove('drag-over');
61
- handleFileSelect({ target: { files: e.dataTransfer.files } });
62
- });
63
-
64
- document.addEventListener('paste', (e) => {
65
- const items = e.clipboardData.items;
66
- for (let i = 0; i < items.length; i++) {
67
- if (items[i].kind === 'file') {
68
- const file = items[i].getAsFile();
69
- handleFileSelect({ target: { files: [file] } });
70
- break;
71
- }
72
- }
73
- });
74
-
75
- span.onclick = function() {
76
- modal.style.display = "none";
77
- }
78
-
79
- window.onclick = function(event) {
80
- if (event.target == modal) {
81
- modal.style.display = "none";
82
- }
83
- }
84
-
85
- function handleFileSelect(e) {
86
- if (e.target.files && e.target.files.length > 0) {
87
- const file = e.target.files[0];
88
- fileName.textContent = file.name;
89
- uploadBtn.style.display = 'inline-block';
90
-
91
- const dataTransfer = new DataTransfer();
92
- dataTransfer.items.add(file);
93
- fileInput.files = dataTransfer.files;
94
- }
95
- }
96
-
97
- async function uploadFile(file) {
98
- const chunkSize = 1024 * 1024; // 1 MB
99
- const totalChunks = Math.ceil(file.size / chunkSize);
100
- const uploadId = generateUploadId();
101
- progressContainer.innerHTML = '';
102
- progressContainer.style.display = 'block';
103
- loadingSpinner.style.display = 'block';
104
- uploadBtn.disabled = true;
105
- resultContainer.innerHTML = '';
106
- resultContainer.style.display = 'none';
107
-
108
- const progressBar = createProgressBar(file.name);
109
- progressContainer.appendChild(progressBar);
110
-
111
- let chunkIndex = 0;
112
-
113
- while (chunkIndex < totalChunks) {
114
- const start = chunkIndex * chunkSize;
115
- const end = Math.min(file.size, start + chunkSize);
116
- const chunk = file.slice(start, end);
117
-
118
- const formData = new FormData();
119
- formData.append('file', chunk);
120
- formData.append('chunkIndex', chunkIndex);
121
- formData.append('totalChunks', totalChunks);
122
- formData.append('uploadId', uploadId);
123
- formData.append('fileName', file.name);
124
- formData.append('contentType', file.type);
125
-
126
- let success = false;
127
-
128
- while (!success) {
129
- try {
130
- await uploadChunk(formData, progressBar.querySelector('.progress'), file.size, start, end);
131
- success = true;
132
- } catch (error) {
133
- console.error('Chunk upload error:', error);
134
- // Wait for a short time before retrying
135
- await new Promise(resolve => setTimeout(resolve, 1000));
136
- }
137
- }
138
-
139
- chunkIndex++;
140
- }
141
-
142
- // After all chunks are uploaded, notify the server to assemble them
143
- const response = await fetch('/assemble', {
144
- method: 'POST',
145
- body: JSON.stringify({ uploadId: uploadId, fileName: file.name, contentType: file.type }),
146
- headers: { 'Content-Type': 'application/json' }
147
- });
148
-
149
- const result = await response.json();
150
- if (response.ok && result.url) {
151
- addResultLink(result.url, file.name);
152
- resetUploadState();
153
- } else {
154
- alert('Failed to assemble file on server.');
155
- }
156
- }
157
-
158
- function generateUploadId() {
159
- return 'xxxxxxx'.replace(/[x]/g, function() {
160
- return Math.floor(Math.random() * 16).toString(16);
161
- });
162
- }
163
-
164
- function createProgressBar(fileName) {
165
- const progressBarContainer = document.createElement('div');
166
- progressBarContainer.className = 'progress-bar';
167
- const progress = document.createElement('div');
168
- progress.className = 'progress';
169
- progressBarContainer.appendChild(progress);
170
- const label = document.createElement('div');
171
- label.textContent = fileName;
172
- label.style.fontSize = '0.8rem';
173
- label.style.marginBottom = '5px';
174
- const container = document.createElement('div');
175
- container.appendChild(label);
176
- container.appendChild(progressBarContainer);
177
- return container;
178
- }
179
-
180
- function resetUploadState() {
181
- fileInput.value = '';
182
- fileName.textContent = '';
183
- uploadBtn.style.display = 'none';
184
- uploadBtn.disabled = false;
185
- loadingSpinner.style.display = 'none';
186
- }
187
-
188
- function updateProgress(event, progressBar) {
189
- if (event.lengthComputable) {
190
- const percentComplete = (event.loaded / event.total) * 100;
191
- progressBar.style.width = percentComplete + '%';
192
- }
193
- }
194
-
195
- function addResultLink(url, fileName) {
196
- const linkContainer = document.createElement('div');
197
- linkContainer.style.marginBottom = '10px';
198
-
199
- const link = document.createElement('a');
200
- link.href = url;
201
- link.textContent = View ${fileName};
202
- link.className = 'result-link';
203
- link.target = 'blank';
204
-
205
- linkContainer.appendChild(link);
206
-
207
- const buttonsContainer = document.createElement('div');
208
- buttonsContainer.className = 'link-buttons';
209
-
210
- const copyBtn = document.createElement('button');
211
- copyBtn.textContent = 'Copy Link';
212
- copyBtn.className = 'small-btn copy-btn';
213
- copyBtn.onclick = () => {
214
- navigator.clipboard.writeText(window.location.origin + url).then(() => {
215
- alert('Link copied to clipboard!');
216
- });
217
- };
218
- buttonsContainer.appendChild(copyBtn);
219
-
220
- if (fileName.toLowerCase().endsWith('.mp4')) {
221
- const embedBtn = document.createElement('button');
222
- embedBtn.textContent = 'Embed Video for Discord';
223
- embedBtn.className = 'small-btn embed-btn';
224
- embedBtn.onclick = () => {
225
- showEmbedModal(url);
226
- };
227
- buttonsContainer.appendChild(embedBtn);
228
- }
229
-
230
- linkContainer.appendChild(buttonsContainer);
231
-
232
- resultContainer.appendChild(linkContainer);
233
- resultContainer.style.display = 'block';
234
- }
235
-
236
- function showEmbedModal(url) {
237
- const embedUrl = ${window.location.origin}/embed?url=${encodeURIComponent(window.location.origin + url)}&thumbnail=${encodeURIComponent('https://coollsd-fileuploader.hf.space/rbxg/LdnShkhZFgMlMmCwkeX78RqHbiol6r554v5BryQS9upEC1wu/Untitled.png')};
238
- embedLinkInput.value = embedUrl;
239
- modal.style.display = "block";
240
- }
241
-
242
- function copyEmbedLink() {
243
- embedLinkInput.select();
244
- document.execCommand('copy');
245
- alert('Embed link copied to clipboard!');
246
- }
247
-
248
- async function uploadChunk(formData, progressBar, totalSize, start, end) {
249
- return new Promise((resolve, reject) => {
250
- const xhr = new XMLHttpRequest();
251
- xhr.open('POST', '/upload_chunk', true);
252
-
253
- xhr.upload.onprogress = (event) => {
254
- if (event.lengthComputable) {
255
- const chunkProgress = (event.loaded / (end - start)) * 100;
256
- const totalProgress = ((start + event.loaded) / totalSize) * 100;
257
- progressBar.style.width = totalProgress + '%';
258
- }
259
- };
260
-
261
- xhr.onload = function() {
262
- if (xhr.status === 200) {
263
- resolve();
264
- } else {
265
- reject(new Error(Chunk upload failed with status ${xhr.status}));
266
- }
267
- };
268
-
269
- xhr.onerror = function() {
270
- reject(new Error('Network error occurred during chunk upload'));
271
- };
272
-
273
- xhr.send(formData);
274
- });
275
- }
276
- </script>
277
- </body>
278
- </html>
279
- """
280
-
281
- @app.get("/", response_class=HTMLResponse)
282
- async def index():
283
- return HTML_CONTENT
284
-
285
- @app.post("/upload_chunk")
286
- async def handle_upload_chunk(request: Request):
287
- form = await request.form()
288
- chunk = form['file'].file
289
- chunk_index = int(form['chunkIndex'])
290
- total_chunks = int(form['totalChunks'])
291
- upload_id = form['uploadId']
292
- file_name = form['fileName']
293
- content_type = form['contentType']
294
-
295
- temp_dir = f"temp_uploads/{upload_id}"
296
- os.makedirs(temp_dir, exist_ok=True)
297
- chunk_path = os.path.join(temp_dir, f"chunk{chunk_index}")
298
- with open(chunk_path, 'wb') as f:
299
- while True:
300
- data = chunk.read(1024 * 1024)
301
- if not data:
302
- break
303
- f.write(data)
304
- return JSONResponse(content={"status": "chunk received"})
305
-
306
- @app.post("/assemble")
307
- async def assemble_chunks(request: Request):
308
- data = await request.json()
309
- upload_id = data['uploadId']
310
- file_name = data['fileName']
311
- content_type = data['contentType']
312
-
313
- temp_dir = f"temp_uploads/{upload_id}"
314
- chunk_files = sorted(os.listdir(temp_dir), key=lambda x: int(x.split('_')[1]))
315
- file_path = os.path.join(temp_dir, file_name)
316
-
317
- with open(file_path, 'wb') as outfile:
318
- for chunk_file in chunk_files:
319
- chunk_path = os.path.join(temp_dir, chunk_file)
320
- with open(chunk_path, 'rb') as infile:
321
- outfile.write(infile.read())
322
-
323
- # Now proceed to upload the file to replicate.com as before
324
- cookies = await get_cookies()
325
-
326
- upload_result = await initiate_upload(cookies, file_name, content_type)
327
- if not upload_result or 'upload_url' not in upload_result:
328
- return JSONResponse(content={"error": "Failed to initiate upload"}, status_code=500)
329
-
330
- # Read the assembled file
331
- with open(file_path, 'rb') as f:
332
- file_content = f.read()
333
-
334
- upload_success = await retry_upload(upload_result['upload_url'], file_content, content_type)
335
- if not upload_success:
336
- return JSONResponse(content={"error": "File upload failed after multiple attempts"}, status_code=500)
337
-
338
- original_url = upload_result['serving_url']
339
- mirrored_url = f"/rbxg/{original_url.split('/pbxt/')[1]}"
340
-
341
- # Clean up temp files
342
- shutil.rmtree(temp_dir)
343
-
344
- return JSONResponse(content={"url": mirrored_url})
345
-
346
- @app.get("/rbxg/{path:path}")
347
- async def handle_video_stream(path: str, request: Request):
348
- original_url = f'https://replicate.delivery/pbxt/{path}'
349
- range_header = request.headers.get('Range')
350
-
351
- headers = {'Range': range_header} if range_header else {}
352
- response = requests.get(original_url, headers=headers, stream=True)
353
-
354
- def generate():
355
- for chunk in response.iter_content(chunk_size=8192):
356
- yield chunk
357
-
358
- headers = dict(response.headers)
359
- headers['Access-Control-Allow-Origin'] = ''
360
- headers['Content-Disposition'] = 'inline'
361
-
362
- if response.status_code == 206:
363
- headers['Content-Range'] = response.headers.get('Content-Range')
364
-
365
- return StreamingResponse(generate(), status_code=response.status_code, headers=headers)
366
-
367
- @app.get("/embed")
368
- async def embed_video(url: str, thumbnail: str):
369
- html = f'''
370
- <html>
371
- <head>
372
- <meta property="og:type" content="video.other">
373
- <meta property="og:video" content="{url}">
374
- <meta property="og:video:url" content="{url}">
375
- <meta property="og:video:secure_url" content="{url}">
376
- <meta property="og:video:type" content="video/mp4">
377
- <meta property="og:video:width" content="1280">
378
- <meta property="og:video:height" content="720">
379
- <meta property="og:image" content="{thumbnail}">
380
- <meta property="og:image:secure_url" content="{thumbnail}">
381
- <meta property="og:image:width" content="1280">
382
- <meta property="og:image:height" content="720">
383
- <meta property="og:image:type" content="image/png">
384
- <style>
385
- body, html {{ margin: 0; padding: 0; height: 100%; background: #000; }}
386
- #thumbnail {{ width: 100%; height: 100%; object-fit: contain; cursor: pointer; }}
387
- #video {{ display: none; width: 100%; height: 100%; object-fit: contain; }}
388
- </style>
389
- </head>
390
- <body>
391
- <img id="thumbnail" src="{thumbnail}" onclick="playVideo()">
392
- <video id="video" controls autoplay>
393
- <source src="{url}" type="video/mp4">
394
- Your browser does not support the video tag.
395
- </video>
396
- <script>
397
- function playVideo() {{
398
- document.getElementById('thumbnail').style.display = 'none';
399
- document.getElementById('video').style.display = 'block';
400
- document.getElementById('video').play();
401
- }}
402
- </script>
403
- </body>
404
- </html>
405
- '''
406
- return HTMLResponse(content=html)
407
-
408
- async def get_cookies() -> Dict[str, str]:
409
- try:
410
- response = requests.get('https://replicate.com/levelsio/neon-tokyo', headers={
411
- 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36'
412
- })
413
- return dict(response.cookies)
414
- except Exception as e:
415
- print(f'Error fetching the page: {e}')
416
- return {}
417
-
418
- async def initiate_upload(cookies: Dict[str, str], filename: str, content_type: str) -> Dict:
419
- url = f'https://replicate.com/api/upload/{filename}?content_type={content_type}'
420
- try:
421
- response = requests.post(url, cookies=cookies, headers={
422
- 'X-CSRFToken': cookies.get('csrftoken'),
423
- 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36',
424
- 'Referer': 'https://replicate.com/levelsio/neon-tokyo',
425
- 'Origin': 'https://replicate.com',
426
- 'Accept': '/',
427
- 'Accept-Language': 'en-US,en;q=0.5',
428
- 'Accept-Encoding': 'identity',
429
- 'Sec-Fetch-Dest': 'empty',
430
- 'Sec-Fetch-Mode': 'cors',
431
- 'Sec-Fetch-Site': 'same-origin',
432
- 'Sec-GPC': '1',
433
- 'Priority': 'u=1, i'
434
- })
435
- return response.json()
436
- except Exception as e:
437
- print(f'Error initiating upload: {e}')
438
- raise
439
-
440
- async def upload_file(upload_url: str, file_content: bytes, content_type: str) -> bool:
441
- try:
442
- response = requests.put(upload_url, data=file_content, headers={'Content-Type': content_type})
443
- return response.status_code == 200
444
- except Exception as e:
445
- print(f'Error uploading file: {e}')
446
- return False
447
-
448
- async def retry_upload(upload_url: str, file_content: bytes, content_type: str, max_retries: int = 5, delay: int = 1) -> bool:
449
- retries = 0
450
- while retries < max_retries:
451
- try:
452
- success = await upload_file(upload_url, file_content, content_type)
453
- if success:
454
- return True
455
- print("Upload failed. Retrying...")
456
- except Exception as e:
457
- print(f"Error during upload: {e}")
458
-
459
- await asyncio.sleep(delay)
460
- delay = min(delay * 2, 60) # Exponential backoff, capped at 60 seconds
461
- retries += 1
462
-
463
- return False
464
-
465
-
466
- Note: This script introduces chunked uploading on the client side and handles chunk assembly on the server side. The client uploads the file in chunks, and if a network error occurs during the upload of a chunk, it will retry that chunk until it succeeds, effectively resuming the upload from where it left off.
467
-
468
- Please make sure to create a directory named temp_uploads in your working directory or update the script to point to a suitable temporary directory for storing the uploaded chunks. Also, ensure that you have the necessary permissions to read and write files in that directory.
469
-
470
- This script replaces the single-file upload mechanism with a chunked upload mechanism to handle network errors gracefully by resuming the upload from the last successful chunk.
471
-
472
- send the entire full script without placehodlers
473
-
474
-
475
- Certainly! Here's the entire full script without any placeholders:
476
-
477
-
478
- from fastapi import FastAPI, File, UploadFile, Request
479
- from fastapi.responses import HTMLResponse, JSONResponse, StreamingResponse
480
- import requests
481
- import time
482
- import asyncio
483
- from typing import Dict
484
- import os
485
- import shutil
486
-
487
- app = FastAPI()
488
-
489
  HTML_CONTENT = """
490
  <!DOCTYPE html>
491
  <html lang="en">
@@ -507,7 +28,6 @@ HTML_CONTENT = """
507
  padding: 20px;
508
  box-sizing: border-box;
509
  }
510
-
511
  body::before {
512
  content: "";
513
  position: fixed;
@@ -520,7 +40,6 @@ HTML_CONTENT = """
520
  opacity: 0.2;
521
  pointer-events: none;
522
  }
523
-
524
  @keyframes grain {
525
  0% { transform: translate(0, 0); }
526
  10% { transform: translate(-5%, -5%); }
@@ -534,7 +53,6 @@ HTML_CONTENT = """
534
  90% { transform: translate(10%, 5%); }
535
  100% { transform: translate(5%, 0); }
536
  }
537
-
538
  .container {
539
  position: relative;
540
  width: 100%;
@@ -548,14 +66,12 @@ HTML_CONTENT = """
548
  text-align: center;
549
  box-shadow: 0 0 20px rgba(0, 0, 0, 0.8);
550
  }
551
-
552
  h1 {
553
  margin-bottom: 1.5rem;
554
  font-size: 1.8rem;
555
  color: #ffffff;
556
  text-shadow: 0 0 5px rgba(255, 255, 255, 0.2);
557
  }
558
-
559
  .btn {
560
  display: inline-block;
561
  position: relative;
@@ -572,12 +88,10 @@ HTML_CONTENT = """
572
  z-index: 1;
573
  transition: color 0.3s ease, box-shadow 0.3s ease;
574
  }
575
-
576
  .btn:hover {
577
  color: #ffffff;
578
  box-shadow: 0 0 15px rgba(200, 200, 200, 0.5);
579
  }
580
-
581
  .btn:hover::before {
582
  content: '';
583
  position: absolute;
@@ -588,17 +102,14 @@ HTML_CONTENT = """
588
  z-index: -1;
589
  pointer-events: none;
590
  }
591
-
592
  @keyframes glowAnimation {
593
  0% { transform: scale(0.8); }
594
  50% { transform: scale(1.2); }
595
  100% { transform: scale(0.8); }
596
  }
597
-
598
  .btn:active {
599
  transform: scale(0.98);
600
  }
601
-
602
  .small-btn {
603
  padding: 6px 12px;
604
  font-size: 0.8rem;
@@ -614,12 +125,10 @@ HTML_CONTENT = """
614
  z-index: 1;
615
  margin: 0.25rem;
616
  }
617
-
618
  .small-btn:hover {
619
  color: #ffffff;
620
  box-shadow: 0 0 10px rgba(200, 200, 200, 0.5);
621
  }
622
-
623
  .small-btn:hover::before {
624
  content: '';
625
  position: absolute;
@@ -630,11 +139,9 @@ HTML_CONTENT = """
630
  z-index: -1;
631
  pointer-events: none;
632
  }
633
-
634
  .small-btn:active {
635
  transform: scale(0.98);
636
  }
637
-
638
  .drop-zone {
639
  position: relative;
640
  padding: 20px;
@@ -646,13 +153,11 @@ HTML_CONTENT = """
646
  background: rgba(255, 255, 255, 0.05);
647
  overflow: hidden;
648
  }
649
-
650
  .drop-zone:hover, .drop-zone.drag-over {
651
  border-color: #ffffff;
652
  background: rgba(255, 255, 255, 0.1);
653
  position: relative;
654
  }
655
-
656
  .drop-zone:hover::before, .drop-zone.drag-over::before {
657
  content: '';
658
  position: absolute;
@@ -663,29 +168,24 @@ HTML_CONTENT = """
663
  z-index: -1;
664
  pointer-events: none;
665
  }
666
-
667
  @keyframes grainGlow {
668
  0% { opacity: 0.2; }
669
  50% { opacity: 0.5; }
670
  100% { opacity: 0.2; }
671
  }
672
-
673
  .file-input {
674
  display: none;
675
  }
676
-
677
  .file-name {
678
  margin-top: 1rem;
679
  font-size: 0.9rem;
680
  color: #aaa;
681
  word-break: break-all;
682
  }
683
-
684
  .progress-container {
685
  display: none;
686
  margin-top: 1.5rem;
687
  }
688
-
689
  .progress-bar {
690
  width: 100%;
691
  height: 10px;
@@ -694,14 +194,12 @@ HTML_CONTENT = """
694
  overflow: hidden;
695
  margin-bottom: 10px;
696
  }
697
-
698
  .progress {
699
  width: 0%;
700
  height: 100%;
701
  background-color: #ffffff;
702
  transition: width 0.3s ease;
703
  }
704
-
705
  .loading-spinner {
706
  display: none;
707
  width: 40px;
@@ -712,17 +210,14 @@ HTML_CONTENT = """
712
  animation: spin 1s linear infinite;
713
  margin: 20px auto;
714
  }
715
-
716
  @keyframes spin {
717
  0% { transform: rotate(0deg); }
718
  100% { transform: rotate(360deg); }
719
  }
720
-
721
  .result-container {
722
  display: none;
723
  margin-top: 1.5rem;
724
  }
725
-
726
  .result-link {
727
  color: #ffffff;
728
  text-decoration: none;
@@ -731,25 +226,21 @@ HTML_CONTENT = """
731
  margin-right: 10px;
732
  word-break: break-all;
733
  }
734
-
735
  .result-link:hover {
736
  text-decoration: underline;
737
  }
738
-
739
  .link-buttons {
740
  display: flex;
741
  justify-content: center;
742
  flex-wrap: wrap;
743
  margin-top: 10px;
744
  }
745
-
746
  /* File Types /
747
  .file-types {
748
  margin-top: 2rem;
749
  font-size: 0.8rem;
750
  color: #aaa;
751
  }
752
-
753
  .modal {
754
  display: none;
755
  position: fixed;
@@ -761,7 +252,6 @@ HTML_CONTENT = """
761
  overflow: auto;
762
  background-color: rgba(0,0,0,0.8);
763
  }
764
-
765
  .modal-content {
766
  background-color: #1e1e1e;
767
  margin: 15% auto;
@@ -774,12 +264,10 @@ HTML_CONTENT = """
774
  animation: modalFadeIn 0.3s;
775
  position: relative;
776
  }
777
-
778
  @keyframes modalFadeIn {
779
  from {opacity: 0; transform: scale(0.8);}
780
  to {opacity: 1; transform: scale(1);}
781
  }
782
-
783
  .close {
784
  color: #aaa;
785
  position: absolute;
@@ -789,21 +277,18 @@ HTML_CONTENT = """
789
  font-weight: bold;
790
  transition: color 0.3s ease;
791
  }
792
-
793
  .close:hover,
794
  .close:focus {
795
  color: #fff;
796
  text-decoration: none;
797
  cursor: pointer;
798
  }
799
-
800
  .embed-container {
801
  display: flex;
802
  flex-direction: column;
803
  align-items: stretch;
804
  margin-top: 15px;
805
  }
806
-
807
  #embedLink {
808
  width: 100%;
809
  padding: 10px;
@@ -814,25 +299,20 @@ HTML_CONTENT = """
814
  margin-bottom: 10px;
815
  font-size: 0.9rem;
816
  }
817
-
818
  @media (max-width: 480px) {
819
  .container {
820
  padding: 1.5rem;
821
  }
822
-
823
  h1 {
824
  font-size: 1.5rem;
825
  }
826
-
827
  .btn, .small-btn {
828
  font-size: 0.9rem;
829
  padding: 10px 20px;
830
  }
831
-
832
  .file-types {
833
  font-size: 0.7rem;
834
  }
835
-
836
  .modal-content {
837
  width: 95%;
838
  margin: 10% auto;
@@ -859,7 +339,6 @@ HTML_CONTENT = """
859
  Allowed file types: .zip, .mp4, .txt, .mp3, all image types, .pdf
860
  </div>
861
  </div>
862
-
863
  <div id="embedModal" class="modal">
864
  <div class="modal-content">
865
  <span class="close">×</span>
@@ -871,7 +350,6 @@ HTML_CONTENT = """
871
  </div>
872
  </div>
873
  </div>
874
-
875
  <script>
876
  const fileInput = document.getElementById('file');
877
  const fileName = document.getElementById('fileName');
@@ -884,31 +362,25 @@ HTML_CONTENT = """
884
  const span = document.getElementsByClassName("close")[0];
885
  const embedLinkInput = document.getElementById('embedLink');
886
  const uploadBtn = document.getElementById('uploadBtn');
887
-
888
  fileInput.addEventListener('change', handleFileSelect);
889
-
890
  uploadForm.addEventListener('submit', (e) => {
891
  e.preventDefault();
892
  if (fileInput.files.length > 0) {
893
  uploadFile(fileInput.files[0]);
894
  }
895
  });
896
-
897
  dropZone.addEventListener('dragover', (e) => {
898
  e.preventDefault();
899
  dropZone.classList.add('drag-over');
900
  });
901
-
902
  dropZone.addEventListener('dragleave', () => {
903
  dropZone.classList.remove('drag-over');
904
  });
905
-
906
  dropZone.addEventListener('drop', (e) => {
907
  e.preventDefault();
908
  dropZone.classList.remove('drag-over');
909
  handleFileSelect({ target: { files: e.dataTransfer.files } });
910
  });
911
-
912
  document.addEventListener('paste', (e) => {
913
  const items = e.clipboardData.items;
914
  for (let i = 0; i < items.length; i++) {
@@ -919,17 +391,14 @@ HTML_CONTENT = """
919
  }
920
  }
921
  });
922
-
923
  span.onclick = function() {
924
  modal.style.display = "none";
925
  }
926
-
927
  window.onclick = function(event) {
928
  if (event.target == modal) {
929
  modal.style.display = "none";
930
  }
931
  }
932
-
933
  function handleFileSelect(e) {
934
  if (e.target.files && e.target.files.length > 0) {
935
  const file = e.target.files[0];
@@ -941,7 +410,6 @@ HTML_CONTENT = """
941
  fileInput.files = dataTransfer.files;
942
  }
943
  }
944
-
945
  async function uploadFile(file) {
946
  const chunkSize = 1024 * 1024; // 1 MB
947
  const totalChunks = Math.ceil(file.size / chunkSize);
@@ -952,17 +420,13 @@ HTML_CONTENT = """
952
  uploadBtn.disabled = true;
953
  resultContainer.innerHTML = '';
954
  resultContainer.style.display = 'none';
955
-
956
  const progressBar = createProgressBar(file.name);
957
  progressContainer.appendChild(progressBar);
958
-
959
  let chunkIndex = 0;
960
-
961
  while (chunkIndex < totalChunks) {
962
  const start = chunkIndex * chunkSize;
963
  const end = Math.min(file.size, start + chunkSize);
964
  const chunk = file.slice(start, end);
965
-
966
  const formData = new FormData();
967
  formData.append('file', chunk);
968
  formData.append('chunkIndex', chunkIndex);
@@ -970,9 +434,7 @@ HTML_CONTENT = """
970
  formData.append('uploadId', uploadId);
971
  formData.append('fileName', file.name);
972
  formData.append('contentType', file.type);
973
-
974
  let success = false;
975
-
976
  while (!success) {
977
  try {
978
  await uploadChunk(formData, progressBar.querySelector('.progress'), file.size, start, end);
@@ -983,17 +445,14 @@ HTML_CONTENT = """
983
  await new Promise(resolve => setTimeout(resolve, 1000));
984
  }
985
  }
986
-
987
  chunkIndex++;
988
  }
989
-
990
  // After all chunks are uploaded, notify the server to assemble them
991
  const response = await fetch('/assemble', {
992
  method: 'POST',
993
  body: JSON.stringify({ uploadId: uploadId, fileName: file.name, contentType: file.type }),
994
  headers: { 'Content-Type': 'application/json' }
995
  });
996
-
997
  const result = await response.json();
998
  if (response.ok && result.url) {
999
  addResultLink(result.url, file.name);
@@ -1002,13 +461,11 @@ HTML_CONTENT = """
1002
  alert('Failed to assemble file on server.');
1003
  }
1004
  }
1005
-
1006
  function generateUploadId() {
1007
  return 'xxxxxxx'.replace(/[x]/g, function() {
1008
  return Math.floor(Math.random() * 16).toString(16);
1009
  });
1010
  }
1011
-
1012
  function createProgressBar(fileName) {
1013
  const progressBarContainer = document.createElement('div');
1014
  progressBarContainer.className = 'progress-bar';
@@ -1024,7 +481,6 @@ HTML_CONTENT = """
1024
  container.appendChild(progressBarContainer);
1025
  return container;
1026
  }
1027
-
1028
  function resetUploadState() {
1029
  fileInput.value = '';
1030
  fileName.textContent = '';
@@ -1032,29 +488,23 @@ HTML_CONTENT = """
1032
  uploadBtn.disabled = false;
1033
  loadingSpinner.style.display = 'none';
1034
  }
1035
-
1036
  function updateProgress(event, progressBar) {
1037
  if (event.lengthComputable) {
1038
  const percentComplete = (event.loaded / event.total) * 100;
1039
  progressBar.style.width = percentComplete + '%';
1040
  }
1041
  }
1042
-
1043
  function addResultLink(url, fileName) {
1044
  const linkContainer = document.createElement('div');
1045
  linkContainer.style.marginBottom = '10px';
1046
-
1047
  const link = document.createElement('a');
1048
  link.href = url;
1049
  link.textContent = View ${fileName};
1050
  link.className = 'result-link';
1051
  link.target = 'blank';
1052
-
1053
  linkContainer.appendChild(link);
1054
-
1055
  const buttonsContainer = document.createElement('div');
1056
  buttonsContainer.className = 'link-buttons';
1057
-
1058
  const copyBtn = document.createElement('button');
1059
  copyBtn.textContent = 'Copy Link';
1060
  copyBtn.className = 'small-btn copy-btn';
@@ -1064,7 +514,6 @@ HTML_CONTENT = """
1064
  });
1065
  };
1066
  buttonsContainer.appendChild(copyBtn);
1067
-
1068
  if (fileName.toLowerCase().endsWith('.mp4')) {
1069
  const embedBtn = document.createElement('button');
1070
  embedBtn.textContent = 'Embed Video for Discord';
@@ -1074,30 +523,24 @@ HTML_CONTENT = """
1074
  };
1075
  buttonsContainer.appendChild(embedBtn);
1076
  }
1077
-
1078
  linkContainer.appendChild(buttonsContainer);
1079
-
1080
  resultContainer.appendChild(linkContainer);
1081
  resultContainer.style.display = 'block';
1082
  }
1083
-
1084
  function showEmbedModal(url) {
1085
  const embedUrl = ${window.location.origin}/embed?url=${encodeURIComponent(window.location.origin + url)}&thumbnail=${encodeURIComponent('https://coollsd-fileuploader.hf.space/rbxg/LdnShkhZFgMlMmCwkeX78RqHbiol6r554v5BryQS9upEC1wu/Untitled.png')};
1086
  embedLinkInput.value = embedUrl;
1087
  modal.style.display = "block";
1088
  }
1089
-
1090
  function copyEmbedLink() {
1091
  embedLinkInput.select();
1092
  document.execCommand('copy');
1093
  alert('Embed link copied to clipboard!');
1094
  }
1095
-
1096
  async function uploadChunk(formData, progressBar, totalSize, start, end) {
1097
  return new Promise((resolve, reject) => {
1098
  const xhr = new XMLHttpRequest();
1099
  xhr.open('POST', '/upload_chunk', true);
1100
-
1101
  xhr.upload.onprogress = (event) => {
1102
  if (event.lengthComputable) {
1103
  const chunkProgress = (event.loaded / (end - start)) * 100;
@@ -1105,7 +548,6 @@ HTML_CONTENT = """
1105
  progressBar.style.width = totalProgress + '%';
1106
  }
1107
  };
1108
-
1109
  xhr.onload = function() {
1110
  if (xhr.status === 200) {
1111
  resolve();
@@ -1113,11 +555,9 @@ HTML_CONTENT = """
1113
  reject(new Error(Chunk upload failed with status ${xhr.status}));
1114
  }
1115
  };
1116
-
1117
  xhr.onerror = function() {
1118
  reject(new Error('Network error occurred during chunk upload'));
1119
  };
1120
-
1121
  xhr.send(formData);
1122
  });
1123
  }
@@ -1125,11 +565,9 @@ HTML_CONTENT = """
1125
  </body>
1126
  </html>
1127
  """
1128
-
1129
  @app.get("/", response_class=HTMLResponse)
1130
  async def index():
1131
  return HTML_CONTENT
1132
-
1133
  @app.post("/upload_chunk")
1134
  async def handle_upload_chunk(request: Request):
1135
  form = await request.form()
@@ -1139,7 +577,6 @@ async def handle_upload_chunk(request: Request):
1139
  upload_id = form['uploadId']
1140
  file_name = form['fileName']
1141
  content_type = form['contentType']
1142
-
1143
  temp_dir = f"temp_uploads/{upload_id}"
1144
  os.makedirs(temp_dir, exist_ok=True)
1145
  chunk_path = os.path.join(temp_dir, f"chunk{chunk_index}")
@@ -1150,68 +587,51 @@ async def handle_upload_chunk(request: Request):
1150
  break
1151
  f.write(data)
1152
  return JSONResponse(content={"status": "chunk received"})
1153
-
1154
  @app.post("/assemble")
1155
  async def assemble_chunks(request: Request):
1156
  data = await request.json()
1157
  upload_id = data['uploadId']
1158
  file_name = data['fileName']
1159
  content_type = data['contentType']
1160
-
1161
  temp_dir = f"temp_uploads/{upload_id}"
1162
  chunk_files = sorted(os.listdir(temp_dir), key=lambda x: int(x.split('_')[1]))
1163
  file_path = os.path.join(temp_dir, file_name)
1164
-
1165
  with open(file_path, 'wb') as outfile:
1166
  for chunk_file in chunk_files:
1167
  chunk_path = os.path.join(temp_dir, chunk_file)
1168
  with open(chunk_path, 'rb') as infile:
1169
  outfile.write(infile.read())
1170
-
1171
  # Now proceed to upload the file to replicate.com as before
1172
  cookies = await get_cookies()
1173
-
1174
  upload_result = await initiate_upload(cookies, file_name, content_type)
1175
  if not upload_result or 'upload_url' not in upload_result:
1176
  return JSONResponse(content={"error": "Failed to initiate upload"}, status_code=500)
1177
-
1178
  # Read the assembled file
1179
  with open(file_path, 'rb') as f:
1180
  file_content = f.read()
1181
-
1182
  upload_success = await retry_upload(upload_result['upload_url'], file_content, content_type)
1183
  if not upload_success:
1184
  return JSONResponse(content={"error": "File upload failed after multiple attempts"}, status_code=500)
1185
-
1186
  original_url = upload_result['serving_url']
1187
  mirrored_url = f"/rbxg/{original_url.split('/pbxt/')[1]}"
1188
-
1189
  # Clean up temp files
1190
  shutil.rmtree(temp_dir)
1191
-
1192
  return JSONResponse(content={"url": mirrored_url})
1193
-
1194
  @app.get("/rbxg/{path:path}")
1195
  async def handle_video_stream(path: str, request: Request):
1196
  original_url = f'https://replicate.delivery/pbxt/{path}'
1197
  range_header = request.headers.get('Range')
1198
-
1199
  headers = {'Range': range_header} if range_header else {}
1200
  response = requests.get(original_url, headers=headers, stream=True)
1201
-
1202
  def generate():
1203
  for chunk in response.iter_content(chunk_size=8192):
1204
  yield chunk
1205
-
1206
  headers = dict(response.headers)
1207
  headers['Access-Control-Allow-Origin'] = ''
1208
  headers['Content-Disposition'] = 'inline'
1209
-
1210
  if response.status_code == 206:
1211
  headers['Content-Range'] = response.headers.get('Content-Range')
1212
-
1213
  return StreamingResponse(generate(), status_code=response.status_code, headers=headers)
1214
-
1215
  @app.get("/embed")
1216
  async def embed_video(url: str, thumbnail: str):
1217
  html = f'''
@@ -1252,7 +672,6 @@ async def embed_video(url: str, thumbnail: str):
1252
  </html>
1253
  '''
1254
  return HTMLResponse(content=html)
1255
-
1256
  async def get_cookies() -> Dict[str, str]:
1257
  try:
1258
  response = requests.get('https://replicate.com/levelsio/neon-tokyo', headers={
@@ -1262,7 +681,6 @@ async def get_cookies() -> Dict[str, str]:
1262
  except Exception as e:
1263
  print(f'Error fetching the page: {e}')
1264
  return {}
1265
-
1266
  async def initiate_upload(cookies: Dict[str, str], filename: str, content_type: str) -> Dict:
1267
  url = f'https://replicate.com/api/upload/{filename}?content_type={content_type}'
1268
  try:
@@ -1284,7 +702,6 @@ async def initiate_upload(cookies: Dict[str, str], filename: str, content_type:
1284
  except Exception as e:
1285
  print(f'Error initiating upload: {e}')
1286
  raise
1287
-
1288
  async def upload_file(upload_url: str, file_content: bytes, content_type: str) -> bool:
1289
  try:
1290
  response = requests.put(upload_url, data=file_content, headers={'Content-Type': content_type})
@@ -1292,7 +709,6 @@ async def upload_file(upload_url: str, file_content: bytes, content_type: str) -
1292
  except Exception as e:
1293
  print(f'Error uploading file: {e}')
1294
  return False
1295
-
1296
  async def retry_upload(upload_url: str, file_content: bytes, content_type: str, max_retries: int = 5, delay: int = 1) -> bool:
1297
  retries = 0
1298
  while retries < max_retries:
@@ -1307,5 +723,4 @@ async def retry_upload(upload_url: str, file_content: bytes, content_type: str,
1307
  await asyncio.sleep(delay)
1308
  delay = min(delay * 2, 60) # Exponential backoff, capped at 60 seconds
1309
  retries += 1
1310
-
1311
  return False
 
6
  from typing import Dict
7
  import os
8
  import shutil
 
9
  app = FastAPI()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
  HTML_CONTENT = """
11
  <!DOCTYPE html>
12
  <html lang="en">
 
28
  padding: 20px;
29
  box-sizing: border-box;
30
  }
 
31
  body::before {
32
  content: "";
33
  position: fixed;
 
40
  opacity: 0.2;
41
  pointer-events: none;
42
  }
 
43
  @keyframes grain {
44
  0% { transform: translate(0, 0); }
45
  10% { transform: translate(-5%, -5%); }
 
53
  90% { transform: translate(10%, 5%); }
54
  100% { transform: translate(5%, 0); }
55
  }
 
56
  .container {
57
  position: relative;
58
  width: 100%;
 
66
  text-align: center;
67
  box-shadow: 0 0 20px rgba(0, 0, 0, 0.8);
68
  }
 
69
  h1 {
70
  margin-bottom: 1.5rem;
71
  font-size: 1.8rem;
72
  color: #ffffff;
73
  text-shadow: 0 0 5px rgba(255, 255, 255, 0.2);
74
  }
 
75
  .btn {
76
  display: inline-block;
77
  position: relative;
 
88
  z-index: 1;
89
  transition: color 0.3s ease, box-shadow 0.3s ease;
90
  }
 
91
  .btn:hover {
92
  color: #ffffff;
93
  box-shadow: 0 0 15px rgba(200, 200, 200, 0.5);
94
  }
 
95
  .btn:hover::before {
96
  content: '';
97
  position: absolute;
 
102
  z-index: -1;
103
  pointer-events: none;
104
  }
 
105
  @keyframes glowAnimation {
106
  0% { transform: scale(0.8); }
107
  50% { transform: scale(1.2); }
108
  100% { transform: scale(0.8); }
109
  }
 
110
  .btn:active {
111
  transform: scale(0.98);
112
  }
 
113
  .small-btn {
114
  padding: 6px 12px;
115
  font-size: 0.8rem;
 
125
  z-index: 1;
126
  margin: 0.25rem;
127
  }
 
128
  .small-btn:hover {
129
  color: #ffffff;
130
  box-shadow: 0 0 10px rgba(200, 200, 200, 0.5);
131
  }
 
132
  .small-btn:hover::before {
133
  content: '';
134
  position: absolute;
 
139
  z-index: -1;
140
  pointer-events: none;
141
  }
 
142
  .small-btn:active {
143
  transform: scale(0.98);
144
  }
 
145
  .drop-zone {
146
  position: relative;
147
  padding: 20px;
 
153
  background: rgba(255, 255, 255, 0.05);
154
  overflow: hidden;
155
  }
 
156
  .drop-zone:hover, .drop-zone.drag-over {
157
  border-color: #ffffff;
158
  background: rgba(255, 255, 255, 0.1);
159
  position: relative;
160
  }
 
161
  .drop-zone:hover::before, .drop-zone.drag-over::before {
162
  content: '';
163
  position: absolute;
 
168
  z-index: -1;
169
  pointer-events: none;
170
  }
 
171
  @keyframes grainGlow {
172
  0% { opacity: 0.2; }
173
  50% { opacity: 0.5; }
174
  100% { opacity: 0.2; }
175
  }
 
176
  .file-input {
177
  display: none;
178
  }
 
179
  .file-name {
180
  margin-top: 1rem;
181
  font-size: 0.9rem;
182
  color: #aaa;
183
  word-break: break-all;
184
  }
 
185
  .progress-container {
186
  display: none;
187
  margin-top: 1.5rem;
188
  }
 
189
  .progress-bar {
190
  width: 100%;
191
  height: 10px;
 
194
  overflow: hidden;
195
  margin-bottom: 10px;
196
  }
 
197
  .progress {
198
  width: 0%;
199
  height: 100%;
200
  background-color: #ffffff;
201
  transition: width 0.3s ease;
202
  }
 
203
  .loading-spinner {
204
  display: none;
205
  width: 40px;
 
210
  animation: spin 1s linear infinite;
211
  margin: 20px auto;
212
  }
 
213
  @keyframes spin {
214
  0% { transform: rotate(0deg); }
215
  100% { transform: rotate(360deg); }
216
  }
 
217
  .result-container {
218
  display: none;
219
  margin-top: 1.5rem;
220
  }
 
221
  .result-link {
222
  color: #ffffff;
223
  text-decoration: none;
 
226
  margin-right: 10px;
227
  word-break: break-all;
228
  }
 
229
  .result-link:hover {
230
  text-decoration: underline;
231
  }
 
232
  .link-buttons {
233
  display: flex;
234
  justify-content: center;
235
  flex-wrap: wrap;
236
  margin-top: 10px;
237
  }
 
238
  /* File Types /
239
  .file-types {
240
  margin-top: 2rem;
241
  font-size: 0.8rem;
242
  color: #aaa;
243
  }
 
244
  .modal {
245
  display: none;
246
  position: fixed;
 
252
  overflow: auto;
253
  background-color: rgba(0,0,0,0.8);
254
  }
 
255
  .modal-content {
256
  background-color: #1e1e1e;
257
  margin: 15% auto;
 
264
  animation: modalFadeIn 0.3s;
265
  position: relative;
266
  }
 
267
  @keyframes modalFadeIn {
268
  from {opacity: 0; transform: scale(0.8);}
269
  to {opacity: 1; transform: scale(1);}
270
  }
 
271
  .close {
272
  color: #aaa;
273
  position: absolute;
 
277
  font-weight: bold;
278
  transition: color 0.3s ease;
279
  }
 
280
  .close:hover,
281
  .close:focus {
282
  color: #fff;
283
  text-decoration: none;
284
  cursor: pointer;
285
  }
 
286
  .embed-container {
287
  display: flex;
288
  flex-direction: column;
289
  align-items: stretch;
290
  margin-top: 15px;
291
  }
 
292
  #embedLink {
293
  width: 100%;
294
  padding: 10px;
 
299
  margin-bottom: 10px;
300
  font-size: 0.9rem;
301
  }
 
302
  @media (max-width: 480px) {
303
  .container {
304
  padding: 1.5rem;
305
  }
 
306
  h1 {
307
  font-size: 1.5rem;
308
  }
 
309
  .btn, .small-btn {
310
  font-size: 0.9rem;
311
  padding: 10px 20px;
312
  }
 
313
  .file-types {
314
  font-size: 0.7rem;
315
  }
 
316
  .modal-content {
317
  width: 95%;
318
  margin: 10% auto;
 
339
  Allowed file types: .zip, .mp4, .txt, .mp3, all image types, .pdf
340
  </div>
341
  </div>
 
342
  <div id="embedModal" class="modal">
343
  <div class="modal-content">
344
  <span class="close">×</span>
 
350
  </div>
351
  </div>
352
  </div>
 
353
  <script>
354
  const fileInput = document.getElementById('file');
355
  const fileName = document.getElementById('fileName');
 
362
  const span = document.getElementsByClassName("close")[0];
363
  const embedLinkInput = document.getElementById('embedLink');
364
  const uploadBtn = document.getElementById('uploadBtn');
 
365
  fileInput.addEventListener('change', handleFileSelect);
 
366
  uploadForm.addEventListener('submit', (e) => {
367
  e.preventDefault();
368
  if (fileInput.files.length > 0) {
369
  uploadFile(fileInput.files[0]);
370
  }
371
  });
 
372
  dropZone.addEventListener('dragover', (e) => {
373
  e.preventDefault();
374
  dropZone.classList.add('drag-over');
375
  });
 
376
  dropZone.addEventListener('dragleave', () => {
377
  dropZone.classList.remove('drag-over');
378
  });
 
379
  dropZone.addEventListener('drop', (e) => {
380
  e.preventDefault();
381
  dropZone.classList.remove('drag-over');
382
  handleFileSelect({ target: { files: e.dataTransfer.files } });
383
  });
 
384
  document.addEventListener('paste', (e) => {
385
  const items = e.clipboardData.items;
386
  for (let i = 0; i < items.length; i++) {
 
391
  }
392
  }
393
  });
 
394
  span.onclick = function() {
395
  modal.style.display = "none";
396
  }
 
397
  window.onclick = function(event) {
398
  if (event.target == modal) {
399
  modal.style.display = "none";
400
  }
401
  }
 
402
  function handleFileSelect(e) {
403
  if (e.target.files && e.target.files.length > 0) {
404
  const file = e.target.files[0];
 
410
  fileInput.files = dataTransfer.files;
411
  }
412
  }
 
413
  async function uploadFile(file) {
414
  const chunkSize = 1024 * 1024; // 1 MB
415
  const totalChunks = Math.ceil(file.size / chunkSize);
 
420
  uploadBtn.disabled = true;
421
  resultContainer.innerHTML = '';
422
  resultContainer.style.display = 'none';
 
423
  const progressBar = createProgressBar(file.name);
424
  progressContainer.appendChild(progressBar);
 
425
  let chunkIndex = 0;
 
426
  while (chunkIndex < totalChunks) {
427
  const start = chunkIndex * chunkSize;
428
  const end = Math.min(file.size, start + chunkSize);
429
  const chunk = file.slice(start, end);
 
430
  const formData = new FormData();
431
  formData.append('file', chunk);
432
  formData.append('chunkIndex', chunkIndex);
 
434
  formData.append('uploadId', uploadId);
435
  formData.append('fileName', file.name);
436
  formData.append('contentType', file.type);
 
437
  let success = false;
 
438
  while (!success) {
439
  try {
440
  await uploadChunk(formData, progressBar.querySelector('.progress'), file.size, start, end);
 
445
  await new Promise(resolve => setTimeout(resolve, 1000));
446
  }
447
  }
 
448
  chunkIndex++;
449
  }
 
450
  // After all chunks are uploaded, notify the server to assemble them
451
  const response = await fetch('/assemble', {
452
  method: 'POST',
453
  body: JSON.stringify({ uploadId: uploadId, fileName: file.name, contentType: file.type }),
454
  headers: { 'Content-Type': 'application/json' }
455
  });
 
456
  const result = await response.json();
457
  if (response.ok && result.url) {
458
  addResultLink(result.url, file.name);
 
461
  alert('Failed to assemble file on server.');
462
  }
463
  }
 
464
  function generateUploadId() {
465
  return 'xxxxxxx'.replace(/[x]/g, function() {
466
  return Math.floor(Math.random() * 16).toString(16);
467
  });
468
  }
 
469
  function createProgressBar(fileName) {
470
  const progressBarContainer = document.createElement('div');
471
  progressBarContainer.className = 'progress-bar';
 
481
  container.appendChild(progressBarContainer);
482
  return container;
483
  }
 
484
  function resetUploadState() {
485
  fileInput.value = '';
486
  fileName.textContent = '';
 
488
  uploadBtn.disabled = false;
489
  loadingSpinner.style.display = 'none';
490
  }
 
491
  function updateProgress(event, progressBar) {
492
  if (event.lengthComputable) {
493
  const percentComplete = (event.loaded / event.total) * 100;
494
  progressBar.style.width = percentComplete + '%';
495
  }
496
  }
 
497
  function addResultLink(url, fileName) {
498
  const linkContainer = document.createElement('div');
499
  linkContainer.style.marginBottom = '10px';
 
500
  const link = document.createElement('a');
501
  link.href = url;
502
  link.textContent = View ${fileName};
503
  link.className = 'result-link';
504
  link.target = 'blank';
 
505
  linkContainer.appendChild(link);
 
506
  const buttonsContainer = document.createElement('div');
507
  buttonsContainer.className = 'link-buttons';
 
508
  const copyBtn = document.createElement('button');
509
  copyBtn.textContent = 'Copy Link';
510
  copyBtn.className = 'small-btn copy-btn';
 
514
  });
515
  };
516
  buttonsContainer.appendChild(copyBtn);
 
517
  if (fileName.toLowerCase().endsWith('.mp4')) {
518
  const embedBtn = document.createElement('button');
519
  embedBtn.textContent = 'Embed Video for Discord';
 
523
  };
524
  buttonsContainer.appendChild(embedBtn);
525
  }
 
526
  linkContainer.appendChild(buttonsContainer);
 
527
  resultContainer.appendChild(linkContainer);
528
  resultContainer.style.display = 'block';
529
  }
 
530
  function showEmbedModal(url) {
531
  const embedUrl = ${window.location.origin}/embed?url=${encodeURIComponent(window.location.origin + url)}&thumbnail=${encodeURIComponent('https://coollsd-fileuploader.hf.space/rbxg/LdnShkhZFgMlMmCwkeX78RqHbiol6r554v5BryQS9upEC1wu/Untitled.png')};
532
  embedLinkInput.value = embedUrl;
533
  modal.style.display = "block";
534
  }
 
535
  function copyEmbedLink() {
536
  embedLinkInput.select();
537
  document.execCommand('copy');
538
  alert('Embed link copied to clipboard!');
539
  }
 
540
  async function uploadChunk(formData, progressBar, totalSize, start, end) {
541
  return new Promise((resolve, reject) => {
542
  const xhr = new XMLHttpRequest();
543
  xhr.open('POST', '/upload_chunk', true);
 
544
  xhr.upload.onprogress = (event) => {
545
  if (event.lengthComputable) {
546
  const chunkProgress = (event.loaded / (end - start)) * 100;
 
548
  progressBar.style.width = totalProgress + '%';
549
  }
550
  };
 
551
  xhr.onload = function() {
552
  if (xhr.status === 200) {
553
  resolve();
 
555
  reject(new Error(Chunk upload failed with status ${xhr.status}));
556
  }
557
  };
 
558
  xhr.onerror = function() {
559
  reject(new Error('Network error occurred during chunk upload'));
560
  };
 
561
  xhr.send(formData);
562
  });
563
  }
 
565
  </body>
566
  </html>
567
  """
 
568
  @app.get("/", response_class=HTMLResponse)
569
  async def index():
570
  return HTML_CONTENT
 
571
  @app.post("/upload_chunk")
572
  async def handle_upload_chunk(request: Request):
573
  form = await request.form()
 
577
  upload_id = form['uploadId']
578
  file_name = form['fileName']
579
  content_type = form['contentType']
 
580
  temp_dir = f"temp_uploads/{upload_id}"
581
  os.makedirs(temp_dir, exist_ok=True)
582
  chunk_path = os.path.join(temp_dir, f"chunk{chunk_index}")
 
587
  break
588
  f.write(data)
589
  return JSONResponse(content={"status": "chunk received"})
 
590
  @app.post("/assemble")
591
  async def assemble_chunks(request: Request):
592
  data = await request.json()
593
  upload_id = data['uploadId']
594
  file_name = data['fileName']
595
  content_type = data['contentType']
 
596
  temp_dir = f"temp_uploads/{upload_id}"
597
  chunk_files = sorted(os.listdir(temp_dir), key=lambda x: int(x.split('_')[1]))
598
  file_path = os.path.join(temp_dir, file_name)
 
599
  with open(file_path, 'wb') as outfile:
600
  for chunk_file in chunk_files:
601
  chunk_path = os.path.join(temp_dir, chunk_file)
602
  with open(chunk_path, 'rb') as infile:
603
  outfile.write(infile.read())
 
604
  # Now proceed to upload the file to replicate.com as before
605
  cookies = await get_cookies()
 
606
  upload_result = await initiate_upload(cookies, file_name, content_type)
607
  if not upload_result or 'upload_url' not in upload_result:
608
  return JSONResponse(content={"error": "Failed to initiate upload"}, status_code=500)
 
609
  # Read the assembled file
610
  with open(file_path, 'rb') as f:
611
  file_content = f.read()
 
612
  upload_success = await retry_upload(upload_result['upload_url'], file_content, content_type)
613
  if not upload_success:
614
  return JSONResponse(content={"error": "File upload failed after multiple attempts"}, status_code=500)
 
615
  original_url = upload_result['serving_url']
616
  mirrored_url = f"/rbxg/{original_url.split('/pbxt/')[1]}"
 
617
  # Clean up temp files
618
  shutil.rmtree(temp_dir)
 
619
  return JSONResponse(content={"url": mirrored_url})
 
620
  @app.get("/rbxg/{path:path}")
621
  async def handle_video_stream(path: str, request: Request):
622
  original_url = f'https://replicate.delivery/pbxt/{path}'
623
  range_header = request.headers.get('Range')
 
624
  headers = {'Range': range_header} if range_header else {}
625
  response = requests.get(original_url, headers=headers, stream=True)
 
626
  def generate():
627
  for chunk in response.iter_content(chunk_size=8192):
628
  yield chunk
 
629
  headers = dict(response.headers)
630
  headers['Access-Control-Allow-Origin'] = ''
631
  headers['Content-Disposition'] = 'inline'
 
632
  if response.status_code == 206:
633
  headers['Content-Range'] = response.headers.get('Content-Range')
 
634
  return StreamingResponse(generate(), status_code=response.status_code, headers=headers)
 
635
  @app.get("/embed")
636
  async def embed_video(url: str, thumbnail: str):
637
  html = f'''
 
672
  </html>
673
  '''
674
  return HTMLResponse(content=html)
 
675
  async def get_cookies() -> Dict[str, str]:
676
  try:
677
  response = requests.get('https://replicate.com/levelsio/neon-tokyo', headers={
 
681
  except Exception as e:
682
  print(f'Error fetching the page: {e}')
683
  return {}
 
684
  async def initiate_upload(cookies: Dict[str, str], filename: str, content_type: str) -> Dict:
685
  url = f'https://replicate.com/api/upload/{filename}?content_type={content_type}'
686
  try:
 
702
  except Exception as e:
703
  print(f'Error initiating upload: {e}')
704
  raise
 
705
  async def upload_file(upload_url: str, file_content: bytes, content_type: str) -> bool:
706
  try:
707
  response = requests.put(upload_url, data=file_content, headers={'Content-Type': content_type})
 
709
  except Exception as e:
710
  print(f'Error uploading file: {e}')
711
  return False
 
712
  async def retry_upload(upload_url: str, file_content: bytes, content_type: str, max_retries: int = 5, delay: int = 1) -> bool:
713
  retries = 0
714
  while retries < max_retries:
 
723
  await asyncio.sleep(delay)
724
  delay = min(delay * 2, 60) # Exponential backoff, capped at 60 seconds
725
  retries += 1
 
726
  return False