coollsd commited on
Commit
548abbb
1 Parent(s): c0ee329

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +167 -133
app.py CHANGED
@@ -1,12 +1,21 @@
1
- from fastapi import FastAPI, File, UploadFile, Request
2
  from fastapi.responses import HTMLResponse, JSONResponse, StreamingResponse
 
3
  import requests
4
  import time
5
  import asyncio
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,6 +37,7 @@ HTML_CONTENT = """
28
  padding: 20px;
29
  box-sizing: border-box;
30
  }
 
31
  body::before {
32
  content: "";
33
  position: fixed;
@@ -40,6 +50,7 @@ HTML_CONTENT = """
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,6 +64,7 @@ HTML_CONTENT = """
53
  90% { transform: translate(10%, 5%); }
54
  100% { transform: translate(5%, 0); }
55
  }
 
56
  .container {
57
  position: relative;
58
  width: 100%;
@@ -66,12 +78,14 @@ HTML_CONTENT = """
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,10 +102,12 @@ HTML_CONTENT = """
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,14 +118,17 @@ HTML_CONTENT = """
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,10 +144,12 @@ HTML_CONTENT = """
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,9 +160,11 @@ HTML_CONTENT = """
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,11 +176,13 @@ HTML_CONTENT = """
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,24 +193,29 @@ HTML_CONTENT = """
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,12 +224,14 @@ HTML_CONTENT = """
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,14 +242,17 @@ HTML_CONTENT = """
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,21 +261,25 @@ HTML_CONTENT = """
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,6 +291,7 @@ HTML_CONTENT = """
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,10 +304,12 @@ HTML_CONTENT = """
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,18 +319,21 @@ HTML_CONTENT = """
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,20 +344,25 @@ HTML_CONTENT = """
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;
@@ -325,7 +375,7 @@ HTML_CONTENT = """
325
  <h1>Radd PRO Uploader</h1>
326
  <form id="uploadForm">
327
  <div id="dropZone" class="drop-zone">
328
- <input type="file" name="file" id="file" class="file-input" accept=".zip,.mp4,.txt,.mp3,image/,.pdf" required>
329
  <label for="file" class="btn">Choose File</label>
330
  <p>or drag and drop file here/paste image</p>
331
  </div>
@@ -339,9 +389,10 @@ HTML_CONTENT = """
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>
345
  <h2>Embed Video Link</h2>
346
  <p>copy the link to embed it on discord:</p>
347
  <div class="embed-container">
@@ -350,37 +401,44 @@ HTML_CONTENT = """
350
  </div>
351
  </div>
352
  </div>
 
353
  <script>
354
  const fileInput = document.getElementById('file');
355
  const fileName = document.getElementById('fileName');
356
  const uploadForm = document.getElementById('uploadForm');
357
  const progressContainer = document.getElementById('progressContainer');
358
  const loadingSpinner = document.getElementById('loadingSpinner');
359
- const resultContainer = document.getElementById('resultContainer');
360
  const dropZone = document.getElementById('dropZone');
361
  const modal = document.getElementById('embedModal');
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,14 +449,17 @@ HTML_CONTENT = """
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,62 +471,58 @@ HTML_CONTENT = """
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);
416
- const uploadId = generateUploadId();
417
  progressContainer.innerHTML = '';
418
  progressContainer.style.display = 'block';
419
  loadingSpinner.style.display = 'block';
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);
433
- formData.append('totalChunks', totalChunks);
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);
441
- success = true;
442
- } catch (error) {
443
- console.error('Chunk upload error:', error);
444
- // Wait for a short time before retrying
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);
 
 
 
 
 
459
  resetUploadState();
460
- } else {
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,6 +538,14 @@ HTML_CONTENT = """
481
  container.appendChild(progressBarContainer);
482
  return container;
483
  }
 
 
 
 
 
 
 
 
484
  function resetUploadState() {
485
  fileInput.value = '';
486
  fileName.textContent = '';
@@ -488,23 +553,22 @@ HTML_CONTENT = """
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,6 +578,7 @@ HTML_CONTENT = """
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,115 +588,79 @@ HTML_CONTENT = """
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;
547
- const totalProgress = ((start + event.loaded) / totalSize) * 100;
548
- progressBar.style.width = totalProgress + '%';
549
- }
550
- };
551
- xhr.onload = function() {
552
- if (xhr.status === 200) {
553
- resolve();
554
- } else {
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
- }
564
  </script>
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()
574
- chunk = form['file'].file
575
- chunk_index = int(form['chunkIndex'])
576
- total_chunks = int(form['totalChunks'])
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}")
583
- with open(chunk_path, 'wb') as f:
584
- while True:
585
- data = chunk.read(1024 * 1024)
586
- if not data:
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,6 +701,7 @@ async def embed_video(url: str, thumbnail: str):
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,6 +711,7 @@ async def get_cookies() -> Dict[str, str]:
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:
@@ -689,7 +720,7 @@ async def initiate_upload(cookies: Dict[str, str], filename: str, content_type:
689
  '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',
690
  'Referer': 'https://replicate.com/levelsio/neon-tokyo',
691
  'Origin': 'https://replicate.com',
692
- 'Accept': '/*',
693
  'Accept-Language': 'en-US,en;q=0.5',
694
  'Accept-Encoding': 'identity',
695
  'Sec-Fetch-Dest': 'empty',
@@ -702,6 +733,7 @@ async def initiate_upload(cookies: Dict[str, str], filename: str, content_type:
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,6 +741,7 @@ async def upload_file(upload_url: str, file_content: bytes, content_type: str) -
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:
@@ -716,11 +749,12 @@ async def retry_upload(upload_url: str, file_content: bytes, content_type: str,
716
  success = await upload_file(upload_url, file_content, content_type)
717
  if success:
718
  return True
719
- print("Upload failed. Retrying...")
720
  except Exception as e:
721
- print(f"Error during upload: {e}")
722
 
 
723
  await asyncio.sleep(delay)
724
  delay = min(delay * 2, 60) # Exponential backoff, capped at 60 seconds
725
- retries += 1
726
  return False
 
1
+ from fastapi import FastAPI, File, UploadFile, Request, HTTPException
2
  from fastapi.responses import HTMLResponse, JSONResponse, StreamingResponse
3
+ from fastapi.middleware.cors import CORSMiddleware
4
  import requests
5
  import time
6
  import asyncio
7
  from typing import Dict
8
+
 
9
  app = FastAPI()
10
+
11
+ app.add_middleware(
12
+ CORSMiddleware,
13
+ allow_origins=["*"],
14
+ allow_credentials=True,
15
+ allow_methods=["*"],
16
+ allow_headers=["*"],
17
+ )
18
+
19
  HTML_CONTENT = """
20
  <!DOCTYPE html>
21
  <html lang="en">
 
37
  padding: 20px;
38
  box-sizing: border-box;
39
  }
40
+
41
  body::before {
42
  content: "";
43
  position: fixed;
 
50
  opacity: 0.2;
51
  pointer-events: none;
52
  }
53
+
54
  @keyframes grain {
55
  0% { transform: translate(0, 0); }
56
  10% { transform: translate(-5%, -5%); }
 
64
  90% { transform: translate(10%, 5%); }
65
  100% { transform: translate(5%, 0); }
66
  }
67
+
68
  .container {
69
  position: relative;
70
  width: 100%;
 
78
  text-align: center;
79
  box-shadow: 0 0 20px rgba(0, 0, 0, 0.8);
80
  }
81
+
82
  h1 {
83
  margin-bottom: 1.5rem;
84
  font-size: 1.8rem;
85
  color: #ffffff;
86
  text-shadow: 0 0 5px rgba(255, 255, 255, 0.2);
87
  }
88
+
89
  .btn {
90
  display: inline-block;
91
  position: relative;
 
102
  z-index: 1;
103
  transition: color 0.3s ease, box-shadow 0.3s ease;
104
  }
105
+
106
  .btn:hover {
107
  color: #ffffff;
108
  box-shadow: 0 0 15px rgba(200, 200, 200, 0.5);
109
  }
110
+
111
  .btn:hover::before {
112
  content: '';
113
  position: absolute;
 
118
  z-index: -1;
119
  pointer-events: none;
120
  }
121
+
122
  @keyframes glowAnimation {
123
  0% { transform: scale(0.8); }
124
  50% { transform: scale(1.2); }
125
  100% { transform: scale(0.8); }
126
  }
127
+
128
  .btn:active {
129
  transform: scale(0.98);
130
  }
131
+
132
  .small-btn {
133
  padding: 6px 12px;
134
  font-size: 0.8rem;
 
144
  z-index: 1;
145
  margin: 0.25rem;
146
  }
147
+
148
  .small-btn:hover {
149
  color: #ffffff;
150
  box-shadow: 0 0 10px rgba(200, 200, 200, 0.5);
151
  }
152
+
153
  .small-btn:hover::before {
154
  content: '';
155
  position: absolute;
 
160
  z-index: -1;
161
  pointer-events: none;
162
  }
163
+
164
  .small-btn:active {
165
  transform: scale(0.98);
166
  }
167
+
168
  .drop-zone {
169
  position: relative;
170
  padding: 20px;
 
176
  background: rgba(255, 255, 255, 0.05);
177
  overflow: hidden;
178
  }
179
+
180
  .drop-zone:hover, .drop-zone.drag-over {
181
  border-color: #ffffff;
182
  background: rgba(255, 255, 255, 0.1);
183
  position: relative;
184
  }
185
+
186
  .drop-zone:hover::before, .drop-zone.drag-over::before {
187
  content: '';
188
  position: absolute;
 
193
  z-index: -1;
194
  pointer-events: none;
195
  }
196
+
197
  @keyframes grainGlow {
198
  0% { opacity: 0.2; }
199
  50% { opacity: 0.5; }
200
  100% { opacity: 0.2; }
201
  }
202
+
203
  .file-input {
204
  display: none;
205
  }
206
+
207
  .file-name {
208
  margin-top: 1rem;
209
  font-size: 0.9rem;
210
  color: #aaa;
211
  word-break: break-all;
212
  }
213
+
214
  .progress-container {
215
  display: none;
216
  margin-top: 1.5rem;
217
  }
218
+
219
  .progress-bar {
220
  width: 100%;
221
  height: 10px;
 
224
  overflow: hidden;
225
  margin-bottom: 10px;
226
  }
227
+
228
  .progress {
229
  width: 0%;
230
  height: 100%;
231
  background-color: #ffffff;
232
  transition: width 0.3s ease;
233
  }
234
+
235
  .loading-spinner {
236
  display: none;
237
  width: 40px;
 
242
  animation: spin 1s linear infinite;
243
  margin: 20px auto;
244
  }
245
+
246
  @keyframes spin {
247
  0% { transform: rotate(0deg); }
248
  100% { transform: rotate(360deg); }
249
  }
250
+
251
  .result-container {
252
  display: none;
253
  margin-top: 1.5rem;
254
  }
255
+
256
  .result-link {
257
  color: #ffffff;
258
  text-decoration: none;
 
261
  margin-right: 10px;
262
  word-break: break-all;
263
  }
264
+
265
  .result-link:hover {
266
  text-decoration: underline;
267
  }
268
+
269
  .link-buttons {
270
  display: flex;
271
  justify-content: center;
272
  flex-wrap: wrap;
273
  margin-top: 10px;
274
  }
275
+
276
+ /* File Types */
277
  .file-types {
278
  margin-top: 2rem;
279
  font-size: 0.8rem;
280
  color: #aaa;
281
  }
282
+
283
  .modal {
284
  display: none;
285
  position: fixed;
 
291
  overflow: auto;
292
  background-color: rgba(0,0,0,0.8);
293
  }
294
+
295
  .modal-content {
296
  background-color: #1e1e1e;
297
  margin: 15% auto;
 
304
  animation: modalFadeIn 0.3s;
305
  position: relative;
306
  }
307
+
308
  @keyframes modalFadeIn {
309
  from {opacity: 0; transform: scale(0.8);}
310
  to {opacity: 1; transform: scale(1);}
311
  }
312
+
313
  .close {
314
  color: #aaa;
315
  position: absolute;
 
319
  font-weight: bold;
320
  transition: color 0.3s ease;
321
  }
322
+
323
  .close:hover,
324
  .close:focus {
325
  color: #fff;
326
  text-decoration: none;
327
  cursor: pointer;
328
  }
329
+
330
  .embed-container {
331
  display: flex;
332
  flex-direction: column;
333
  align-items: stretch;
334
  margin-top: 15px;
335
  }
336
+
337
  #embedLink {
338
  width: 100%;
339
  padding: 10px;
 
344
  margin-bottom: 10px;
345
  font-size: 0.9rem;
346
  }
347
+
348
  @media (max-width: 480px) {
349
  .container {
350
  padding: 1.5rem;
351
  }
352
+
353
  h1 {
354
  font-size: 1.5rem;
355
  }
356
+
357
  .btn, .small-btn {
358
  font-size: 0.9rem;
359
  padding: 10px 20px;
360
  }
361
+
362
  .file-types {
363
  font-size: 0.7rem;
364
  }
365
+
366
  .modal-content {
367
  width: 95%;
368
  margin: 10% auto;
 
375
  <h1>Radd PRO Uploader</h1>
376
  <form id="uploadForm">
377
  <div id="dropZone" class="drop-zone">
378
+ <input type="file" name="file" id="file" class="file-input" accept=".zip,.mp4,.txt,.mp3,image/*,.pdf" required>
379
  <label for="file" class="btn">Choose File</label>
380
  <p>or drag and drop file here/paste image</p>
381
  </div>
 
389
  Allowed file types: .zip, .mp4, .txt, .mp3, all image types, .pdf
390
  </div>
391
  </div>
392
+
393
  <div id="embedModal" class="modal">
394
  <div class="modal-content">
395
+ <span class="close">&times;</span>
396
  <h2>Embed Video Link</h2>
397
  <p>copy the link to embed it on discord:</p>
398
  <div class="embed-container">
 
401
  </div>
402
  </div>
403
  </div>
404
+
405
  <script>
406
  const fileInput = document.getElementById('file');
407
  const fileName = document.getElementById('fileName');
408
  const uploadForm = document.getElementById('uploadForm');
409
  const progressContainer = document.getElementById('progressContainer');
410
  const loadingSpinner = document.getElementById('loadingSpinner');
411
+ const resultContainer = document.getElementById('resultContainer');
412
  const dropZone = document.getElementById('dropZone');
413
  const modal = document.getElementById('embedModal');
414
  const span = document.getElementsByClassName("close")[0];
415
  const embedLinkInput = document.getElementById('embedLink');
416
  const uploadBtn = document.getElementById('uploadBtn');
417
+
418
  fileInput.addEventListener('change', handleFileSelect);
419
+
420
+ uploadForm.addEventListener('submit', async (e) => {
421
  e.preventDefault();
422
  if (fileInput.files.length > 0) {
423
+ await uploadFile(fileInput.files[0]);
424
  }
425
  });
426
+
427
  dropZone.addEventListener('dragover', (e) => {
428
  e.preventDefault();
429
  dropZone.classList.add('drag-over');
430
  });
431
+
432
  dropZone.addEventListener('dragleave', () => {
433
  dropZone.classList.remove('drag-over');
434
  });
435
+
436
  dropZone.addEventListener('drop', (e) => {
437
  e.preventDefault();
438
  dropZone.classList.remove('drag-over');
439
  handleFileSelect({ target: { files: e.dataTransfer.files } });
440
  });
441
+
442
  document.addEventListener('paste', (e) => {
443
  const items = e.clipboardData.items;
444
  for (let i = 0; i < items.length; i++) {
 
449
  }
450
  }
451
  });
452
+
453
  span.onclick = function() {
454
  modal.style.display = "none";
455
  }
456
+
457
  window.onclick = function(event) {
458
  if (event.target == modal) {
459
  modal.style.display = "none";
460
  }
461
  }
462
+
463
  function handleFileSelect(e) {
464
  if (e.target.files && e.target.files.length > 0) {
465
  const file = e.target.files[0];
 
471
  fileInput.files = dataTransfer.files;
472
  }
473
  }
474
+
475
  async function uploadFile(file) {
 
 
 
476
  progressContainer.innerHTML = '';
477
  progressContainer.style.display = 'block';
478
  loadingSpinner.style.display = 'block';
479
  uploadBtn.disabled = true;
480
  resultContainer.innerHTML = '';
481
  resultContainer.style.display = 'none';
482
+
483
  const progressBar = createProgressBar(file.name);
484
  progressContainer.appendChild(progressBar);
485
+
486
+ const formData = new FormData();
487
+ formData.append('file', file);
488
+
489
+ try {
490
+ const xhr = new XMLHttpRequest();
491
+ xhr.open('POST', '/upload', true);
492
+ xhr.upload.onprogress = (event) => updateProgress(event, progressBar.querySelector('.progress'));
493
+
494
+ xhr.onload = function() {
495
+ if (xhr.status === 200) {
496
+ const response = JSON.parse(xhr.responseText);
497
+ if (response.url) {
498
+ addResultLink(response.url, file.name);
499
+ } else {
500
+ throw new Error('Upload failed: ' + response.error);
501
+ }
502
+ } else {
503
+ throw new Error(`HTTP error! status: ${xhr.status}`);
 
 
504
  }
505
+ };
506
+
507
+ xhr.onerror = function() {
508
+ throw new Error('Network error occurred');
509
+ };
510
+
511
+ xhr.send(formData);
512
+
513
+ await new Promise((resolve, reject) => {
514
+ xhr.onloadend = resolve;
515
+ xhr.onerror = reject;
516
+ });
517
+
518
+ } catch (error) {
519
+ console.error('Upload error:', error);
520
+ alert('Upload failed: ' + error.message);
521
+ } finally {
522
  resetUploadState();
 
 
523
  }
524
  }
525
+
 
 
 
 
526
  function createProgressBar(fileName) {
527
  const progressBarContainer = document.createElement('div');
528
  progressBarContainer.className = 'progress-bar';
 
538
  container.appendChild(progressBarContainer);
539
  return container;
540
  }
541
+
542
+ function updateProgress(event, progressBar) {
543
+ if (event.lengthComputable) {
544
+ const percentComplete = (event.loaded / event.total) * 100;
545
+ progressBar.style.width = percentComplete + '%';
546
+ }
547
+ }
548
+
549
  function resetUploadState() {
550
  fileInput.value = '';
551
  fileName.textContent = '';
 
553
  uploadBtn.disabled = false;
554
  loadingSpinner.style.display = 'none';
555
  }
556
+
 
 
 
 
 
557
  function addResultLink(url, fileName) {
558
  const linkContainer = document.createElement('div');
559
  linkContainer.style.marginBottom = '10px';
560
+
561
  const link = document.createElement('a');
562
  link.href = url;
563
+ link.textContent = `View ${fileName}`;
564
  link.className = 'result-link';
565
+ link.target = '_blank';
566
+
567
  linkContainer.appendChild(link);
568
+
569
  const buttonsContainer = document.createElement('div');
570
  buttonsContainer.className = 'link-buttons';
571
+
572
  const copyBtn = document.createElement('button');
573
  copyBtn.textContent = 'Copy Link';
574
  copyBtn.className = 'small-btn copy-btn';
 
578
  });
579
  };
580
  buttonsContainer.appendChild(copyBtn);
581
+
582
  if (fileName.toLowerCase().endsWith('.mp4')) {
583
  const embedBtn = document.createElement('button');
584
  embedBtn.textContent = 'Embed Video for Discord';
 
588
  };
589
  buttonsContainer.appendChild(embedBtn);
590
  }
591
+
592
  linkContainer.appendChild(buttonsContainer);
593
+
594
  resultContainer.appendChild(linkContainer);
595
  resultContainer.style.display = 'block';
596
  }
597
+
598
  function showEmbedModal(url) {
599
+ const embedUrl = `${window.location.origin}/embed?url=${encodeURIComponent(window.location.origin + url)}&thumbnail=${encodeURIComponent('https://coollsd-fileuploader.hf.space/rbxg/LdnShkhZFgMlMmCwkeX78RqHbiol6r554v5BryQS9upEC1wu/Untitled.png')}`;
600
  embedLinkInput.value = embedUrl;
601
  modal.style.display = "block";
602
  }
603
+
604
  function copyEmbedLink() {
605
  embedLinkInput.select();
606
  document.execCommand('copy');
607
  alert('Embed link copied to clipboard!');
608
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
609
  </script>
610
  </body>
611
  </html>
612
  """
613
+
614
  @app.get("/", response_class=HTMLResponse)
615
  async def index():
616
  return HTML_CONTENT
617
+
618
+ @app.post("/upload")
619
+ async def handle_upload(file: UploadFile = File(...)):
620
+ if not file:
621
+ raise HTTPException(status_code=400, detail="No file part")
622
+ if file.filename == '':
623
+ raise HTTPException(status_code=400, detail="No selected file")
624
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
625
  cookies = await get_cookies()
626
+ if 'csrftoken' not in cookies or 'sessionid' not in cookies:
627
+ raise HTTPException(status_code=500, detail="Failed to obtain necessary cookies")
628
+
629
+ upload_result = await initiate_upload(cookies, file.filename, file.content_type)
630
  if not upload_result or 'upload_url' not in upload_result:
631
+ raise HTTPException(status_code=500, detail="Failed to initiate upload")
632
+
633
+ file_content = await file.read()
634
+ upload_success = await retry_upload(upload_result['upload_url'], file_content, file.content_type)
 
635
  if not upload_success:
636
+ raise HTTPException(status_code=500, detail="File upload failed after multiple attempts")
637
+
638
  original_url = upload_result['serving_url']
639
  mirrored_url = f"/rbxg/{original_url.split('/pbxt/')[1]}"
640
+
 
641
  return JSONResponse(content={"url": mirrored_url})
642
+
643
  @app.get("/rbxg/{path:path}")
644
  async def handle_video_stream(path: str, request: Request):
645
  original_url = f'https://replicate.delivery/pbxt/{path}'
646
  range_header = request.headers.get('Range')
647
+
648
  headers = {'Range': range_header} if range_header else {}
649
  response = requests.get(original_url, headers=headers, stream=True)
650
+
651
  def generate():
652
  for chunk in response.iter_content(chunk_size=8192):
653
  yield chunk
654
+
655
  headers = dict(response.headers)
656
+ headers['Access-Control-Allow-Origin'] = '*'
657
  headers['Content-Disposition'] = 'inline'
658
+
659
  if response.status_code == 206:
660
  headers['Content-Range'] = response.headers.get('Content-Range')
661
+
662
  return StreamingResponse(generate(), status_code=response.status_code, headers=headers)
663
+
664
  @app.get("/embed")
665
  async def embed_video(url: str, thumbnail: str):
666
  html = f'''
 
701
  </html>
702
  '''
703
  return HTMLResponse(content=html)
704
+
705
  async def get_cookies() -> Dict[str, str]:
706
  try:
707
  response = requests.get('https://replicate.com/levelsio/neon-tokyo', headers={
 
711
  except Exception as e:
712
  print(f'Error fetching the page: {e}')
713
  return {}
714
+
715
  async def initiate_upload(cookies: Dict[str, str], filename: str, content_type: str) -> Dict:
716
  url = f'https://replicate.com/api/upload/{filename}?content_type={content_type}'
717
  try:
 
720
  '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',
721
  'Referer': 'https://replicate.com/levelsio/neon-tokyo',
722
  'Origin': 'https://replicate.com',
723
+ 'Accept': '*/*',
724
  'Accept-Language': 'en-US,en;q=0.5',
725
  'Accept-Encoding': 'identity',
726
  'Sec-Fetch-Dest': 'empty',
 
733
  except Exception as e:
734
  print(f'Error initiating upload: {e}')
735
  raise
736
+
737
  async def upload_file(upload_url: str, file_content: bytes, content_type: str) -> bool:
738
  try:
739
  response = requests.put(upload_url, data=file_content, headers={'Content-Type': content_type})
 
741
  except Exception as e:
742
  print(f'Error uploading file: {e}')
743
  return False
744
+
745
  async def retry_upload(upload_url: str, file_content: bytes, content_type: str, max_retries: int = 5, delay: int = 1) -> bool:
746
  retries = 0
747
  while retries < max_retries:
 
749
  success = await upload_file(upload_url, file_content, content_type)
750
  if success:
751
  return True
752
+ print(f"Upload attempt {retries + 1} failed. Retrying...")
753
  except Exception as e:
754
+ print(f"Error during upload attempt {retries + 1}: {e}")
755
 
756
+ retries += 1
757
  await asyncio.sleep(delay)
758
  delay = min(delay * 2, 60) # Exponential backoff, capped at 60 seconds
759
+
760
  return False