coollsd commited on
Commit
ebde77a
1 Parent(s): d82c03c

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +277 -483
app.py CHANGED
@@ -1,7 +1,6 @@
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
 
@@ -12,328 +11,245 @@ HTML_CONTENT = """
12
  <html lang="en">
13
  <head>
14
  <meta charset="UTF-8">
15
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
16
  <title>Radd PRO Uploader</title>
17
- <link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;600&display=swap" rel="stylesheet">
 
 
 
 
18
  <style>
19
- body {
20
- font-family: 'Poppins', sans-serif;
21
- background-color: #121212;
22
- color: #e0e0e0;
23
  margin: 0;
24
- min-height: 100vh;
25
- display: flex;
26
- justify-content: center;
27
- align-items: center;
28
- padding: 20px;
29
  box-sizing: border-box;
30
  }
31
 
32
- body::before {
33
- content: "";
34
- position: fixed;
35
- top: 0;
36
- left: 0;
37
- width: 100%;
38
- height: 100%;
39
- background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAAA8CAQAAACM/9unAAAACXBIWXMAAAsTAAALEwEAmpwYAAABF0lEQVR4nO3YMRLCQBRE0clsYgxZwwq7oLGxAgp2AtbsqQZsYgEG0wEWc/LSZnb9HcMjR8fHvEKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgsK8tJOq9fzs7vlkNlWvf5ELi+20t5k2VSL7p/iHJ5Rz5ynOhU7rQ4pUi+6b4hyeUc+cpzoVO60OkV7rQ4pUi+6b4hxenu+en4rQAOkaks/eJegGaUfOHI+nRvQCND1w2J9yf8C2KZwNjkeUsAKRa7Nz50tQAokVbO/U6AaiWjl5wd6kQAOke8Xpx3JgTQKVq7Heh0ADpFvF6cd0kE0Claux3odAA6RfxenHc+BNCpWrsd6HQAOkX8Xpx3PgTQqVq7Heh0ADpF/F6cdz4E0KlarjdI/cR1FBQUGBgYGBgYGBgYG/AT4AIHph6aQGU7oAAAAASUVORK5CYII=') repeat;
40
- animation: grain 8s steps(10) infinite;
41
- opacity: 0.2;
42
- pointer-events: none;
43
  }
44
 
45
- @keyframes grain {
46
- 0% { transform: translate(0, 0); }
47
- 10% { transform: translate(-5%, -5%); }
48
- 20% { transform: translate(-10%, 5%); }
49
- 30% { transform: translate(5%, -10%); }
50
- 40% { transform: translate(-5%, 15%); }
51
- 50% { transform: translate(-10%, 5%); }
52
- 60% { transform: translate(15%, 0); }
53
- 70% { transform: translate(0, 10%); }
54
- 80% { transform: translate(-15%, 0); }
55
- 90% { transform: translate(10%, 5%); }
56
- 100% { transform: translate(5%, 0); }
57
  }
58
 
59
  .container {
60
- position: relative;
61
- width: 100%;
62
- max-width: 450px;
63
- margin: 0 auto;
64
  padding: 2rem;
65
- background: rgba(18, 18, 18, 0.9);
66
  backdrop-filter: blur(10px);
67
- border-radius: 15px;
68
- z-index: 1;
69
  text-align: center;
70
- box-shadow: 0 0 20px rgba(0, 0, 0, 0.8);
71
- }
72
-
73
- h1 {
74
- margin-bottom: 1.5rem;
75
- font-size: 1.8rem;
76
- color: #ffffff;
77
- text-shadow: 0 0 5px rgba(255, 255, 255, 0.2);
78
  }
79
 
80
- .btn {
81
- display: inline-block;
82
- position: relative;
83
- padding: 12px 24px;
84
- margin: 0.5rem;
85
- font-size: 1rem;
86
- font-weight: 600;
87
- color: #ffffff;
88
- background-color: #2a2a2a;
89
- border: none;
90
- border-radius: 5px;
91
  cursor: pointer;
 
 
92
  overflow: hidden;
93
- z-index: 1;
94
- transition: color 0.3s ease, box-shadow 0.3s ease;
95
  }
96
 
97
- .btn:hover {
98
- color: #ffffff;
99
- box-shadow: 0 0 15px rgba(200, 200, 200, 0.5);
100
  }
101
 
102
- .btn:hover::before {
103
- content: '';
104
  position: absolute;
105
- inset: -10px;
106
- background: radial-gradient(circle at center, rgba(200,200,200,0.2), transparent);
107
- filter: blur(20px);
108
- animation: glowAnimation 2s infinite;
109
- z-index: -1;
 
110
  pointer-events: none;
 
111
  }
112
 
113
- @keyframes glowAnimation {
114
- 0% { transform: scale(0.8); }
115
- 50% { transform: scale(1.2); }
116
- 100% { transform: scale(0.8); }
117
  }
118
 
119
- .btn:active {
120
- transform: scale(0.98);
 
121
  }
122
 
123
- .small-btn {
124
- padding: 6px 12px;
125
- font-size: 0.8rem;
126
- font-weight: 500;
127
- background-color: #2a2a2a;
128
- color: #ffffff;
129
- border: none;
130
- border-radius: 5px;
131
- cursor: pointer;
132
- transition: color 0.3s ease, box-shadow 0.3s ease;
133
- position: relative;
134
- overflow: hidden;
135
- z-index: 1;
136
- margin: 0.25rem;
137
- }
138
-
139
- .small-btn:hover {
140
- color: #ffffff;
141
- box-shadow: 0 0 10px rgba(200, 200, 200, 0.5);
142
- }
143
-
144
- .small-btn:hover::before {
145
- content: '';
146
- position: absolute;
147
- inset: -10px;
148
- background: radial-gradient(circle at center, rgba(200,200,200,0.2), transparent);
149
- filter: blur(15px);
150
- animation: glowAnimation 2s infinite;
151
- z-index: -1;
152
- pointer-events: none;
153
  }
154
 
155
- .small-btn:active {
156
- transform: scale(0.98);
157
  }
158
 
159
- .drop-zone {
160
- position: relative;
161
- padding: 20px;
162
- margin-bottom: 1rem;
163
- border: 2px dashed #aaa;
164
- border-radius: 10px;
 
 
 
 
165
  cursor: pointer;
166
- transition: all 0.3s ease;
167
- background: rgba(255, 255, 255, 0.05);
168
- overflow: hidden;
169
- }
170
-
171
- .drop-zone:hover, .drop-zone.drag-over {
172
- border-color: #ffffff;
173
- background: rgba(255, 255, 255, 0.1);
174
  position: relative;
 
175
  }
176
 
177
- .drop-zone:hover::before, .drop-zone.drag-over::before {
178
- content: '';
179
  position: absolute;
180
- inset: -10px;
181
- background: radial-gradient(circle at center, rgba(200,200,200,0.2), transparent);
182
- filter: blur(30px);
183
- animation: grainGlow 5s infinite;
184
- z-index: -1;
185
- pointer-events: none;
186
- }
187
-
188
- @keyframes grainGlow {
189
- 0% { opacity: 0.2; }
190
- 50% { opacity: 0.5; }
191
- 100% { opacity: 0.2; }
192
  }
193
 
194
- .file-input {
195
- display: none;
196
  }
197
 
198
- .file-name {
199
- margin-top: 1rem;
200
- font-size: 0.9rem;
201
- color: #aaa;
202
- word-break: break-all;
203
  }
204
 
205
  .progress-container {
206
- display: none;
207
  margin-top: 1.5rem;
 
208
  }
209
 
210
  .progress-bar {
211
  width: 100%;
212
  height: 10px;
213
- background-color: #333;
214
- border-radius: 5px;
215
  overflow: hidden;
216
- margin-bottom: 10px;
217
  }
218
 
219
  .progress {
220
  width: 0%;
221
  height: 100%;
222
- background-color: #ffffff;
 
223
  transition: width 0.3s ease;
224
  }
225
 
226
- .loading-spinner {
227
- display: none;
228
- width: 40px;
229
- height: 40px;
230
- border: 4px solid #333;
231
- border-top: 4px solid #ffffff;
232
- border-radius: 50%;
233
- animation: spin 1s linear infinite;
234
- margin: 20px auto;
235
- }
236
-
237
- @keyframes spin {
238
- 0% { transform: rotate(0deg); }
239
- 100% { transform: rotate(360deg); }
240
- }
241
-
242
  .result-container {
243
- display: none;
244
  margin-top: 1.5rem;
 
 
245
  }
246
 
247
  .result-link {
248
- color: #ffffff;
249
  text-decoration: none;
250
- font-weight: 600;
251
- transition: all 0.3s ease;
252
- margin-right: 10px;
253
- word-break: break-all;
254
  }
255
 
256
  .result-link:hover {
257
- text-decoration: underline;
258
- }
259
-
260
- .link-buttons {
261
- display: flex;
262
- justify-content: center;
263
- flex-wrap: wrap;
264
- margin-top: 10px;
265
  }
266
 
267
- /* File Types */
268
- .file-types {
269
- margin-top: 2rem;
270
- font-size: 0.8rem;
271
- color: #aaa;
272
  }
273
 
274
- .modal {
275
- display: none;
276
- position: fixed;
277
- z-index: 2;
278
- left: 0;
279
- top: 0;
280
- width: 100%;
281
- height: 100%;
282
- overflow: auto;
283
- background-color: rgba(0,0,0,0.8);
284
  }
285
 
286
- .modal-content {
287
- background-color: #1e1e1e;
288
- margin: 15% auto;
289
- padding: 20px;
290
- border: 1px solid #333;
291
- width: 90%;
292
- max-width: 600px;
293
- border-radius: 10px;
294
- color: #e0e0e0;
295
- animation: modalFadeIn 0.3s;
296
- position: relative;
297
  }
298
 
299
- @keyframes modalFadeIn {
300
- from {opacity: 0; transform: scale(0.8);}
301
- to {opacity: 1; transform: scale(1);}
 
 
 
 
 
 
302
  }
303
 
304
- .close {
305
- color: #aaa;
306
- position: absolute;
307
- top: 10px;
308
- right: 15px;
309
- font-size: 28px;
310
- font-weight: bold;
311
- transition: color 0.3s ease;
 
312
  }
313
 
314
- .close:hover,
315
- .close:focus {
316
- color: #fff;
317
- text-decoration: none;
318
- cursor: pointer;
 
 
319
  }
320
 
321
- .embed-container {
322
- display: flex;
323
- flex-direction: column;
324
- align-items: stretch;
325
- margin-top: 15px;
326
  }
327
 
328
- #embedLink {
329
- width: 100%;
330
- padding: 10px;
331
- background-color: #333;
332
- border: 1px solid #555;
333
- color: #e0e0e0;
334
- border-radius: 5px;
335
- margin-bottom: 10px;
336
- font-size: 0.9rem;
 
337
  }
338
 
339
  @media (max-width: 480px) {
@@ -344,20 +260,6 @@ HTML_CONTENT = """
344
  h1 {
345
  font-size: 1.5rem;
346
  }
347
-
348
- .btn, .small-btn {
349
- font-size: 0.9rem;
350
- padding: 10px 20px;
351
- }
352
-
353
- .file-types {
354
- font-size: 0.7rem;
355
- }
356
-
357
- .modal-content {
358
- width: 95%;
359
- margin: 10% auto;
360
- }
361
  }
362
  </style>
363
  </head>
@@ -366,13 +268,16 @@ HTML_CONTENT = """
366
  <h1>Radd PRO Uploader</h1>
367
  <form id="uploadForm">
368
  <div id="dropZone" class="drop-zone">
 
 
369
  <input type="file" name="file" id="file" class="file-input" accept=".zip,.mp4,.txt,.mp3,image/*,.pdf" required>
370
- <label for="file" class="btn">Choose File</label>
371
- <p>or drag and drop file here/paste image</p>
372
  </div>
373
- <div class="file-name" id="fileName"></div>
374
- <button type="submit" id="uploadBtn" class="btn" style="display: none; margin-top: 1rem;">Upload File</button>
375
- <div class="progress-container" id="progressContainer"></div>
 
 
 
376
  <div class="loading-spinner" id="loadingSpinner"></div>
377
  </form>
378
  <div class="result-container" id="resultContainer"></div>
@@ -381,228 +286,116 @@ HTML_CONTENT = """
381
  </div>
382
  </div>
383
 
384
- <div id="embedModal" class="modal">
385
- <div class="modal-content">
386
- <span class="close">&times;</span>
387
- <h2>Embed Video Link</h2>
388
- <p>copy the link to embed it on discord:</p>
389
- <div class="embed-container">
390
- <input type="text" id="embedLink" readonly>
391
- <button onclick="copyEmbedLink()" class="small-btn copy-embed-btn">Copy</button>
392
- </div>
393
- </div>
394
- </div>
395
-
396
  <script>
397
  const fileInput = document.getElementById('file');
398
- const fileName = document.getElementById('fileName');
399
  const uploadForm = document.getElementById('uploadForm');
400
  const progressContainer = document.getElementById('progressContainer');
 
401
  const loadingSpinner = document.getElementById('loadingSpinner');
402
  const resultContainer = document.getElementById('resultContainer');
403
  const dropZone = document.getElementById('dropZone');
404
- const modal = document.getElementById('embedModal');
405
- const span = document.getElementsByClassName("close")[0];
406
- const embedLinkInput = document.getElementById('embedLink');
407
  const uploadBtn = document.getElementById('uploadBtn');
408
 
409
- fileInput.addEventListener('change', handleFileSelect);
410
-
411
- uploadForm.addEventListener('submit', (e) => {
412
- e.preventDefault();
413
- if (fileInput.files.length > 0) {
414
- uploadFile(fileInput.files[0]);
415
- }
416
  });
417
 
418
- dropZone.addEventListener('dragover', (e) => {
419
- e.preventDefault();
420
- dropZone.classList.add('drag-over');
 
 
 
421
  });
422
 
423
- dropZone.addEventListener('dragleave', () => {
424
- dropZone.classList.remove('drag-over');
 
 
 
 
425
  });
426
 
427
  dropZone.addEventListener('drop', (e) => {
428
  e.preventDefault();
429
- dropZone.classList.remove('drag-over');
430
- handleFileSelect({ target: { files: e.dataTransfer.files } });
431
- });
432
-
433
- document.addEventListener('paste', (e) => {
434
- const items = e.clipboardData.items;
435
- for (let i = 0; i < items.length; i++) {
436
- if (items[i].kind === 'file') {
437
- const file = items[i].getAsFile();
438
- handleFileSelect({ target: { files: [file] } });
439
- break;
440
- }
441
- }
442
  });
443
 
444
- span.onclick = function() {
445
- modal.style.display = "none";
446
- }
447
-
448
- window.onclick = function(event) {
449
- if (event.target == modal) {
450
- modal.style.display = "none";
451
- }
452
- }
453
 
454
- function handleFileSelect(e) {
455
- if (e.target.files && e.target.files.length > 0) {
456
- const file = e.target.files[0];
457
- fileName.textContent = file.name;
458
- uploadBtn.style.display = 'inline-block';
459
-
460
  const dataTransfer = new DataTransfer();
461
- dataTransfer.items.add(file);
462
  fileInput.files = dataTransfer.files;
 
 
463
  }
464
  }
465
 
 
 
 
 
 
 
 
466
  async function uploadFile(file) {
467
- progressContainer.innerHTML = '';
468
  progressContainer.style.display = 'block';
469
- loadingSpinner.style.display = 'block';
470
  uploadBtn.disabled = true;
471
- resultContainer.innerHTML = '';
472
  resultContainer.style.display = 'none';
473
-
474
- const progressBar = createProgressBar(file.name);
475
- progressContainer.appendChild(progressBar);
476
 
477
  const formData = new FormData();
478
  formData.append('file', file);
479
 
480
- while (true) {
481
- try {
482
- const xhr = new XMLHttpRequest();
483
- xhr.open('POST', '/upload', true);
484
- xhr.upload.onprogress = (event) => updateProgress(event, progressBar.querySelector('.progress'));
485
-
486
- xhr.onload = function() {
487
- if (xhr.status === 200) {
488
- const response = JSON.parse(xhr.responseText);
489
- if (response.url) {
490
- addResultLink(response.url, file.name);
491
- resetUploadState();
492
- return;
493
- } else {
494
- throw new Error('Upload failed: ' + response.error);
495
- }
496
- } else {
497
- throw new Error(`HTTP error! status: ${xhr.status}`);
498
- }
499
- };
500
-
501
- xhr.onerror = function() {
502
- throw new Error('Network error occurred');
503
- };
504
-
505
- xhr.send(formData);
506
-
507
- // Wait for the request to complete
508
- await new Promise((resolve, reject) => {
509
- xhr.onloadend = resolve;
510
- xhr.onerror = reject;
511
- });
512
-
513
- break; // Success, exit the loop
514
- } catch (error) {
515
- console.error('Upload error:', error);
516
- // Wait for a short time before retrying
517
- await new Promise(resolve => setTimeout(resolve, 1000));
518
- // The loop will continue, retrying the upload
519
  }
520
- }
521
- }
522
 
523
- function createProgressBar(fileName) {
524
- const progressBarContainer = document.createElement('div');
525
- progressBarContainer.className = 'progress-bar';
526
- const progress = document.createElement('div');
527
- progress.className = 'progress';
528
- progressBarContainer.appendChild(progress);
529
- const label = document.createElement('div');
530
- label.textContent = fileName;
531
- label.style.fontSize = '0.8rem';
532
- label.style.marginBottom = '5px';
533
- const container = document.createElement('div');
534
- container.appendChild(label);
535
- container.appendChild(progressBarContainer);
536
- return container;
537
- }
538
-
539
- function updateProgress(event, progressBar) {
540
- if (event.lengthComputable) {
541
- const percentComplete = (event.loaded / event.total) * 100;
542
- progressBar.style.width = percentComplete + '%';
543
- }
544
  }
545
 
546
- function resetUploadState() {
547
  fileInput.value = '';
548
- fileName.textContent = '';
549
  uploadBtn.style.display = 'none';
550
  uploadBtn.disabled = false;
551
  loadingSpinner.style.display = 'none';
 
552
  }
553
 
554
- function addResultLink(url, fileName) {
555
- const linkContainer = document.createElement('div');
556
- linkContainer.style.marginBottom = '10px';
557
-
558
- const link = document.createElement('a');
559
- link.href = url;
560
- link.textContent = `View ${fileName}`;
561
- link.className = 'result-link';
562
- link.target = '_blank';
563
-
564
- linkContainer.appendChild(link);
565
-
566
- const buttonsContainer = document.createElement('div');
567
- buttonsContainer.className = 'link-buttons';
568
-
569
- const copyBtn = document.createElement('button');
570
- copyBtn.textContent = 'Copy Link';
571
- copyBtn.className = 'small-btn copy-btn';
572
- copyBtn.onclick = () => {
573
- navigator.clipboard.writeText(window.location.origin + url).then(() => {
574
- alert('Link copied to clipboard!');
575
- });
576
- };
577
- buttonsContainer.appendChild(copyBtn);
578
-
579
- if (fileName.toLowerCase().endsWith('.mp4')) {
580
- const embedBtn = document.createElement('button');
581
- embedBtn.textContent = 'Embed Video for Discord';
582
- embedBtn.className = 'small-btn embed-btn';
583
- embedBtn.onclick = () => {
584
- showEmbedModal(url);
585
- };
586
- buttonsContainer.appendChild(embedBtn);
587
- }
588
-
589
- linkContainer.appendChild(buttonsContainer);
590
-
591
- resultContainer.appendChild(linkContainer);
592
  resultContainer.style.display = 'block';
593
  }
594
-
595
- function showEmbedModal(url) {
596
- const embedUrl = `${window.location.origin}/embed?url=${encodeURIComponent(window.location.origin + url)}&thumbnail=${encodeURIComponent('https://coollsd-fileuploader.hf.space/rbxg/LdnShkhZFgMlMmCwkeX78RqHbiol6r554v5BryQS9upEC1wu/Untitled.png')}`;
597
- embedLinkInput.value = embedUrl;
598
- modal.style.display = "block";
599
- }
600
-
601
- function copyEmbedLink() {
602
- embedLinkInput.select();
603
- document.execCommand('copy');
604
- alert('Embed link copied to clipboard!');
605
- }
606
  </script>
607
  </body>
608
  </html>
@@ -643,23 +436,21 @@ async def handle_video_stream(path: str, request: Request):
643
  headers = {'Range': range_header} if range_header else {}
644
  response = requests.get(original_url, headers=headers, stream=True)
645
 
646
- def generate():
647
  for chunk in response.iter_content(chunk_size=8192):
648
  yield chunk
649
 
650
- headers = dict(response.headers)
651
- headers['Access-Control-Allow-Origin'] = '*'
652
- headers['Content-Disposition'] = 'inline'
653
 
654
- if response.status_code == 206:
655
- headers['Content-Range'] = response.headers.get('Content-Range')
656
-
657
- return StreamingResponse(generate(), status_code=response.status_code, headers=headers)
658
 
659
  @app.get("/embed")
660
  async def embed_video(url: str, thumbnail: str):
661
  html = f'''
662
- <html>
 
663
  <head>
664
  <meta property="og:type" content="video.other">
665
  <meta property="og:video" content="{url}">
@@ -674,24 +465,42 @@ async def embed_video(url: str, thumbnail: str):
674
  <meta property="og:image:height" content="720">
675
  <meta property="og:image:type" content="image/png">
676
  <style>
677
- body, html {{ margin: 0; padding: 0; height: 100%; background: #000; }}
678
- #thumbnail {{ width: 100%; height: 100%; object-fit: contain; cursor: pointer; }}
679
- #video {{ display: none; width: 100%; height: 100%; object-fit: contain; }}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
680
  </style>
681
  </head>
682
  <body>
683
- <img id="thumbnail" src="{thumbnail}" onclick="playVideo()">
684
- <video id="video" controls autoplay>
685
- <source src="{url}" type="video/mp4">
686
- Your browser does not support the video tag.
687
- </video>
688
- <script>
689
- function playVideo() {{
690
- document.getElementById('thumbnail').style.display = 'none';
691
- document.getElementById('video').style.display = 'block';
692
- document.getElementById('video').play();
693
- }}
694
- </script>
695
  </body>
696
  </html>
697
  '''
@@ -700,9 +509,9 @@ async def embed_video(url: str, thumbnail: str):
700
  async def get_cookies() -> Dict[str, str]:
701
  try:
702
  response = requests.get('https://replicate.com/levelsio/neon-tokyo', headers={
703
- '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'
704
  })
705
- return dict(response.cookies)
706
  except Exception as e:
707
  print(f'Error fetching the page: {e}')
708
  return {}
@@ -712,22 +521,12 @@ async def initiate_upload(cookies: Dict[str, str], filename: str, content_type:
712
  try:
713
  response = requests.post(url, cookies=cookies, headers={
714
  'X-CSRFToken': cookies.get('csrftoken'),
715
- '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',
716
- 'Referer': 'https://replicate.com/levelsio/neon-tokyo',
717
- 'Origin': 'https://replicate.com',
718
- 'Accept': '*/*',
719
- 'Accept-Language': 'en-US,en;q=0.5',
720
- 'Accept-Encoding': 'identity',
721
- 'Sec-Fetch-Dest': 'empty',
722
- 'Sec-Fetch-Mode': 'cors',
723
- 'Sec-Fetch-Site': 'same-origin',
724
- 'Sec-GPC': '1',
725
- 'Priority': 'u=1, i'
726
  })
727
  return response.json()
728
  except Exception as e:
729
  print(f'Error initiating upload: {e}')
730
- raise
731
 
732
  async def upload_file(upload_url: str, file_content: bytes, content_type: str) -> bool:
733
  try:
@@ -738,16 +537,11 @@ async def upload_file(upload_url: str, file_content: bytes, content_type: str) -
738
  return False
739
 
740
  async def retry_upload(upload_url: str, file_content: bytes, content_type: str, max_retries: int = 5, delay: int = 1) -> bool:
741
- while True:
742
- try:
743
- success = await upload_file(upload_url, file_content, content_type)
744
- if success:
745
- return True
746
- print("Upload failed. Retrying...")
747
- except Exception as e:
748
- print(f"Error during upload: {e}")
749
-
750
- await asyncio.sleep(delay)
751
- delay = min(delay * 2, 60) # Exponential backoff, capped at 60 seconds
752
-
753
  return False
 
1
  from fastapi import FastAPI, File, UploadFile, Request
2
  from fastapi.responses import HTMLResponse, JSONResponse, StreamingResponse
3
  import requests
 
4
  import asyncio
5
  from typing import Dict
6
 
 
11
  <html lang="en">
12
  <head>
13
  <meta charset="UTF-8">
 
14
  <title>Radd PRO Uploader</title>
15
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
16
+ <!-- Google Fonts and Icons -->
17
+ <link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;700&display=swap" rel="stylesheet">
18
+ <!-- Material Icons -->
19
+ <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
20
  <style>
21
+ /* Reset and base styles */
22
+ * {
 
 
23
  margin: 0;
24
+ padding: 0;
 
 
 
 
25
  box-sizing: border-box;
26
  }
27
 
28
+ body {
29
+ font-family: 'Roboto', sans-serif;
30
+ background: linear-gradient(135deg, #667eea, #764ba2);
31
+ color: #fff;
32
+ min-height: 100vh;
33
+ display: flex;
34
+ align-items: center;
35
+ justify-content: center;
36
+ overflow: hidden;
 
 
37
  }
38
 
39
+ h1 {
40
+ font-size: 2rem;
41
+ margin-bottom: 1rem;
42
+ text-align: center;
43
+ animation: fadeInDown 1s ease forwards;
 
 
 
 
 
 
 
44
  }
45
 
46
  .container {
47
+ background: rgba(255, 255, 255, 0.1);
 
 
 
48
  padding: 2rem;
49
+ border-radius: 16px;
50
  backdrop-filter: blur(10px);
51
+ max-width: 500px;
52
+ width: 90%;
53
  text-align: center;
54
+ animation: fadeInUp 1s ease forwards;
 
 
 
 
 
 
 
55
  }
56
 
57
+ .drop-zone {
58
+ border: 2px dashed rgba(255, 255, 255, 0.5);
59
+ border-radius: 12px;
60
+ padding: 2rem;
 
 
 
 
 
 
 
61
  cursor: pointer;
62
+ transition: background 0.3s, border-color 0.3s;
63
+ position: relative;
64
  overflow: hidden;
 
 
65
  }
66
 
67
+ .drop-zone.hover {
68
+ background: rgba(255, 255, 255, 0.1);
69
+ border-color: #fff;
70
  }
71
 
72
+ .drop-zone::before {
73
+ content: "";
74
  position: absolute;
75
+ top: -50%;
76
+ left: -50%;
77
+ width: 200%;
78
+ height: 200%;
79
+ background: radial-gradient(circle at center, rgba(255, 255, 255, 0.2), transparent 70%);
80
+ opacity: 0;
81
  pointer-events: none;
82
+ transition: opacity 0.5s;
83
  }
84
 
85
+ .drop-zone.hover::before {
86
+ opacity: 1;
87
+ animation: pulsate 1.5s infinite;
 
88
  }
89
 
90
+ .drop-zone i {
91
+ font-size: 4rem;
92
+ color: rgba(255, 255, 255, 0.7);
93
  }
94
 
95
+ .drop-zone p {
96
+ margin-top: 1rem;
97
+ font-size: 1rem;
98
+ color: rgba(255, 255, 255, 0.8);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
99
  }
100
 
101
+ .file-input {
102
+ display: none;
103
  }
104
 
105
+ .btn {
106
+ margin-top: 1.5rem;
107
+ padding: 1rem 2rem;
108
+ background: #6a11cb;
109
+ background: linear-gradient(to right, #2575fc, #6a11cb);
110
+ border: none;
111
+ border-radius: 50px;
112
+ color: #fff;
113
+ font-size: 1rem;
114
+ font-weight: 500;
115
  cursor: pointer;
116
+ box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
117
+ transition: transform 0.2s ease, box-shadow 0.2s ease;
 
 
 
 
 
 
118
  position: relative;
119
+ overflow: hidden;
120
  }
121
 
122
+ .btn::before {
123
+ content: "";
124
  position: absolute;
125
+ top: 0;
126
+ left: -100%;
127
+ width: 100%;
128
+ height: 100%;
129
+ background: linear-gradient(to right, rgba(255,255,255,0.2), rgba(255,255,255,0.1), rgba(255,255,255,0));
130
+ transform: skewX(-45deg);
131
+ transition: left 0.5s;
 
 
 
 
 
132
  }
133
 
134
+ .btn:hover::before {
135
+ left: 100%;
136
  }
137
 
138
+ .btn:hover {
139
+ transform: scale(1.02);
140
+ box-shadow: 0 6px 20px rgba(0, 0, 0, 0.3);
 
 
141
  }
142
 
143
  .progress-container {
 
144
  margin-top: 1.5rem;
145
+ display: none;
146
  }
147
 
148
  .progress-bar {
149
  width: 100%;
150
  height: 10px;
151
+ background: rgba(255, 255, 255, 0.2);
152
+ border-radius: 50px;
153
  overflow: hidden;
 
154
  }
155
 
156
  .progress {
157
  width: 0%;
158
  height: 100%;
159
+ background: linear-gradient(to right, #36d1dc, #5b86e5);
160
+ border-radius: 50px;
161
  transition: width 0.3s ease;
162
  }
163
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
164
  .result-container {
 
165
  margin-top: 1.5rem;
166
+ display: none;
167
+ animation: fadeIn 1s ease forwards;
168
  }
169
 
170
  .result-link {
171
+ color: #fff;
172
  text-decoration: none;
173
+ word-wrap: break-word;
174
+ font-weight: 500;
175
+ transition: color 0.3s;
 
176
  }
177
 
178
  .result-link:hover {
179
+ color: #c7c7c7;
 
 
 
 
 
 
 
180
  }
181
 
182
+ .loading-spinner {
183
+ margin-top: 1.5rem;
184
+ display: none;
 
 
185
  }
186
 
187
+ .loading-spinner::after {
188
+ content: '';
189
+ width: 40px;
190
+ height: 40px;
191
+ border: 6px solid rgba(255, 255, 255, 0.2);
192
+ border-top-color: #fff;
193
+ border-radius: 50%;
194
+ animation: spin 1s linear infinite;
195
+ display: inline-block;
 
196
  }
197
 
198
+ .file-types {
199
+ margin-top: 2rem;
200
+ font-size: 0.9rem;
201
+ color: rgba(255, 255, 255, 0.7);
202
+ animation: fadeIn 1s ease forwards;
203
+ animation-delay: 0.5s;
 
 
 
 
 
204
  }
205
 
206
+ @keyframes fadeInDown {
207
+ from {
208
+ opacity: 0;
209
+ transform: translateY(-20px);
210
+ }
211
+ to {
212
+ opacity: 1;
213
+ transform: translateY(0);
214
+ }
215
  }
216
 
217
+ @keyframes fadeInUp {
218
+ from {
219
+ opacity: 0;
220
+ transform: translateY(20px);
221
+ }
222
+ to {
223
+ opacity: 1;
224
+ transform: translateY(0);
225
+ }
226
  }
227
 
228
+ @keyframes fadeIn {
229
+ from {
230
+ opacity: 0;
231
+ }
232
+ to {
233
+ opacity: 1;
234
+ }
235
  }
236
 
237
+ @keyframes spin {
238
+ to {
239
+ transform: rotate(360deg);
240
+ }
 
241
  }
242
 
243
+ @keyframes pulsate {
244
+ 0% {
245
+ opacity: 0.2;
246
+ }
247
+ 50% {
248
+ opacity: 0.4;
249
+ }
250
+ 100% {
251
+ opacity: 0.2;
252
+ }
253
  }
254
 
255
  @media (max-width: 480px) {
 
260
  h1 {
261
  font-size: 1.5rem;
262
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
263
  }
264
  </style>
265
  </head>
 
268
  <h1>Radd PRO Uploader</h1>
269
  <form id="uploadForm">
270
  <div id="dropZone" class="drop-zone">
271
+ <i class="material-icons">cloud_upload</i>
272
+ <p>Drag & Drop or Click to Upload</p>
273
  <input type="file" name="file" id="file" class="file-input" accept=".zip,.mp4,.txt,.mp3,image/*,.pdf" required>
 
 
274
  </div>
275
+ <button type="submit" id="uploadBtn" class="btn" style="display: none;">Upload File</button>
276
+ <div class="progress-container" id="progressContainer">
277
+ <div class="progress-bar">
278
+ <div class="progress" id="progress"></div>
279
+ </div>
280
+ </div>
281
  <div class="loading-spinner" id="loadingSpinner"></div>
282
  </form>
283
  <div class="result-container" id="resultContainer"></div>
 
286
  </div>
287
  </div>
288
 
 
 
 
 
 
 
 
 
 
 
 
 
289
  <script>
290
  const fileInput = document.getElementById('file');
 
291
  const uploadForm = document.getElementById('uploadForm');
292
  const progressContainer = document.getElementById('progressContainer');
293
+ const progressBar = document.getElementById('progress');
294
  const loadingSpinner = document.getElementById('loadingSpinner');
295
  const resultContainer = document.getElementById('resultContainer');
296
  const dropZone = document.getElementById('dropZone');
 
 
 
297
  const uploadBtn = document.getElementById('uploadBtn');
298
 
299
+ dropZone.addEventListener('click', () => {
300
+ fileInput.click();
 
 
 
 
 
301
  });
302
 
303
+ ['dragenter', 'dragover'].forEach(eventName => {
304
+ dropZone.addEventListener(eventName, (e) => {
305
+ e.preventDefault();
306
+ e.stopPropagation();
307
+ dropZone.classList.add('hover');
308
+ });
309
  });
310
 
311
+ ['dragleave', 'drop'].forEach(eventName => {
312
+ dropZone.addEventListener(eventName, (e) => {
313
+ e.preventDefault();
314
+ e.stopPropagation();
315
+ dropZone.classList.remove('hover');
316
+ });
317
  });
318
 
319
  dropZone.addEventListener('drop', (e) => {
320
  e.preventDefault();
321
+ fileInput.files = e.dataTransfer.files;
322
+ handleFileSelect();
 
 
 
 
 
 
 
 
 
 
 
323
  });
324
 
325
+ fileInput.addEventListener('change', handleFileSelect);
 
 
 
 
 
 
 
 
326
 
327
+ function handleFileSelect() {
328
+ if (fileInput.files && fileInput.files.length > 0) {
329
+ uploadBtn.style.display = 'block';
 
 
 
330
  const dataTransfer = new DataTransfer();
331
+ dataTransfer.items.add(fileInput.files[0]);
332
  fileInput.files = dataTransfer.files;
333
+ } else {
334
+ uploadBtn.style.display = 'none';
335
  }
336
  }
337
 
338
+ uploadForm.addEventListener('submit', (e) => {
339
+ e.preventDefault();
340
+ if (fileInput.files.length > 0) {
341
+ uploadFile(fileInput.files[0]);
342
+ }
343
+ });
344
+
345
  async function uploadFile(file) {
 
346
  progressContainer.style.display = 'block';
347
+ loadingSpinner.style.display = 'inline-block';
348
  uploadBtn.disabled = true;
 
349
  resultContainer.style.display = 'none';
350
+ progressBar.style.width = '0%';
 
 
351
 
352
  const formData = new FormData();
353
  formData.append('file', file);
354
 
355
+ const xhr = new XMLHttpRequest();
356
+ xhr.open('POST', '/upload', true);
357
+
358
+ xhr.upload.onprogress = (event) => {
359
+ if (event.lengthComputable) {
360
+ const percentComplete = ((event.loaded / event.total) * 100).toFixed(2);
361
+ progressBar.style.width = percentComplete + '%';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
362
  }
363
+ };
 
364
 
365
+ xhr.onload = function () {
366
+ if (xhr.status === 200) {
367
+ const response = JSON.parse(xhr.responseText);
368
+ displayResult(response.url, file.name);
369
+ resetForm();
370
+ } else {
371
+ alert('An error occurred during the upload. Please try again.');
372
+ resetForm();
373
+ }
374
+ };
375
+
376
+ xhr.onerror = function () {
377
+ alert('An error occurred during the upload. Please try again.');
378
+ resetForm();
379
+ };
380
+
381
+ xhr.send(formData);
 
 
 
 
382
  }
383
 
384
+ function resetForm() {
385
  fileInput.value = '';
 
386
  uploadBtn.style.display = 'none';
387
  uploadBtn.disabled = false;
388
  loadingSpinner.style.display = 'none';
389
+ progressContainer.style.display = 'none';
390
  }
391
 
392
+ function displayResult(url, fileName) {
393
+ resultContainer.innerHTML = `
394
+ <p>File uploaded successfully:</p>
395
+ <a href="${url}" target="_blank" class="result-link">${fileName}</a>
396
+ `;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
397
  resultContainer.style.display = 'block';
398
  }
 
 
 
 
 
 
 
 
 
 
 
 
399
  </script>
400
  </body>
401
  </html>
 
436
  headers = {'Range': range_header} if range_header else {}
437
  response = requests.get(original_url, headers=headers, stream=True)
438
 
439
+ async def generate():
440
  for chunk in response.iter_content(chunk_size=8192):
441
  yield chunk
442
 
443
+ response_headers = {key: value for key, value in response.headers.items()}
444
+ response_headers['Access-Control-Allow-Origin'] = '*'
445
+ response_headers['Content-Disposition'] = 'inline'
446
 
447
+ return StreamingResponse(generate(), status_code=response.status_code, headers=response_headers)
 
 
 
448
 
449
  @app.get("/embed")
450
  async def embed_video(url: str, thumbnail: str):
451
  html = f'''
452
+ <!DOCTYPE html>
453
+ <html lang="en">
454
  <head>
455
  <meta property="og:type" content="video.other">
456
  <meta property="og:video" content="{url}">
 
465
  <meta property="og:image:height" content="720">
466
  <meta property="og:image:type" content="image/png">
467
  <style>
468
+ body, html {{
469
+ margin: 0;
470
+ padding: 0;
471
+ width: 100%;
472
+ height: 100%;
473
+ background: #000;
474
+ display: flex;
475
+ align-items: center;
476
+ justify-content: center;
477
+ }}
478
+
479
+ #video {{
480
+ width: 100%;
481
+ height: 100%;
482
+ }}
483
+
484
+ .video-container {{
485
+ position: relative;
486
+ width: 100%;
487
+ max-width: 1280px;
488
+ height: auto;
489
+ }}
490
+
491
+ .video-container video {{
492
+ width: 100%;
493
+ height: auto;
494
+ }}
495
  </style>
496
  </head>
497
  <body>
498
+ <div class="video-container">
499
+ <video id="video" controls autoplay>
500
+ <source src="{url}" type="video/mp4">
501
+ Your browser does not support the video tag.
502
+ </video>
503
+ </div>
 
 
 
 
 
 
504
  </body>
505
  </html>
506
  '''
 
509
  async def get_cookies() -> Dict[str, str]:
510
  try:
511
  response = requests.get('https://replicate.com/levelsio/neon-tokyo', headers={
512
+ 'User-Agent': 'Mozilla/5.0'
513
  })
514
+ return response.cookies.get_dict()
515
  except Exception as e:
516
  print(f'Error fetching the page: {e}')
517
  return {}
 
521
  try:
522
  response = requests.post(url, cookies=cookies, headers={
523
  'X-CSRFToken': cookies.get('csrftoken'),
524
+ 'User-Agent': 'Mozilla/5.0'
 
 
 
 
 
 
 
 
 
 
525
  })
526
  return response.json()
527
  except Exception as e:
528
  print(f'Error initiating upload: {e}')
529
+ return {}
530
 
531
  async def upload_file(upload_url: str, file_content: bytes, content_type: str) -> bool:
532
  try:
 
537
  return False
538
 
539
  async def retry_upload(upload_url: str, file_content: bytes, content_type: str, max_retries: int = 5, delay: int = 1) -> bool:
540
+ for attempt in range(1, max_retries + 1):
541
+ success = await upload_file(upload_url, file_content, content_type)
542
+ if success:
543
+ return True
544
+ else:
545
+ print(f"Upload failed on attempt {attempt}, retrying in {delay} seconds...")
546
+ await asyncio.sleep(delay)
 
 
 
 
 
547
  return False