coollsd commited on
Commit
910736e
·
verified ·
1 Parent(s): 9e4646e

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +629 -58
app.py CHANGED
@@ -1,20 +1,490 @@
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>
@@ -273,7 +743,7 @@ HTML_CONTENT = """
273
  margin-top: 10px;
274
  }
275
 
276
- /* File Types */
277
  .file-types {
278
  margin-top: 2rem;
279
  font-size: 0.8rem;
@@ -375,7 +845,7 @@ HTML_CONTENT = """
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>
@@ -392,7 +862,7 @@ HTML_CONTENT = """
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">
@@ -409,7 +879,7 @@ HTML_CONTENT = """
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');
@@ -417,10 +887,10 @@ HTML_CONTENT = """
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
 
@@ -473,6 +943,9 @@ HTML_CONTENT = """
473
  }
474
 
475
  async function uploadFile(file) {
 
 
 
476
  progressContainer.innerHTML = '';
477
  progressContainer.style.display = 'block';
478
  loadingSpinner.style.display = 'block';
@@ -483,33 +956,59 @@ HTML_CONTENT = """
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 response = await fetch('/upload', {
491
- method: 'POST',
492
- body: formData
493
- });
494
 
495
- if (!response.ok) {
496
- throw new Error(`HTTP error! status: ${response.status}`);
497
- }
 
 
 
498
 
499
- const result = await response.json();
500
- if (result.url) {
501
- addResultLink(result.url, file.name);
502
- } else {
503
- throw new Error('Upload failed: ' + result.error);
504
- }
505
- } catch (error) {
506
- console.error('Upload error:', error);
507
- alert('Upload failed: ' + error.message);
508
- } finally {
509
  resetUploadState();
 
 
510
  }
511
  }
512
 
 
 
 
 
 
 
513
  function createProgressBar(fileName) {
514
  const progressBarContainer = document.createElement('div');
515
  progressBarContainer.className = 'progress-bar';
@@ -534,15 +1033,22 @@ HTML_CONTENT = """
534
  loadingSpinner.style.display = 'none';
535
  }
536
 
 
 
 
 
 
 
 
537
  function addResultLink(url, fileName) {
538
  const linkContainer = document.createElement('div');
539
  linkContainer.style.marginBottom = '10px';
540
 
541
  const link = document.createElement('a');
542
  link.href = url;
543
- link.textContent = `View ${fileName}`;
544
  link.className = 'result-link';
545
- link.target = '_blank';
546
 
547
  linkContainer.appendChild(link);
548
 
@@ -576,7 +1082,7 @@ HTML_CONTENT = """
576
  }
577
 
578
  function showEmbedModal(url) {
579
- const embedUrl = `${window.location.origin}/embed?url=${encodeURIComponent(window.location.origin + url)}&thumbnail=${encodeURIComponent('https://coollsd-fileuploader.hf.space/rbxg/LdnShkhZFgMlMmCwkeX78RqHbiol6r554v5BryQS9upEC1wu/Untitled.png')}`;
580
  embedLinkInput.value = embedUrl;
581
  modal.style.display = "block";
582
  }
@@ -586,6 +1092,35 @@ HTML_CONTENT = """
586
  document.execCommand('copy');
587
  alert('Embed link copied to clipboard!');
588
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
589
  </script>
590
  </body>
591
  </html>
@@ -595,29 +1130,65 @@ HTML_CONTENT = """
595
  async def index():
596
  return HTML_CONTENT
597
 
598
- @app.post("/upload")
599
- async def handle_upload(file: UploadFile = File(...)):
600
- if not file:
601
- raise HTTPException(status_code=400, detail="No file part")
602
- if file.filename == '':
603
- raise HTTPException(status_code=400, detail="No selected file")
604
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
605
  cookies = await get_cookies()
606
- if 'csrftoken' not in cookies or 'sessionid' not in cookies:
607
- raise HTTPException(status_code=500, detail="Failed to obtain necessary cookies")
608
 
609
- upload_result = await initiate_upload(cookies, file.filename, file.content_type)
610
  if not upload_result or 'upload_url' not in upload_result:
611
- raise HTTPException(status_code=500, detail="Failed to initiate upload")
612
 
613
- file_content = await file.read()
614
- upload_success = await retry_upload(upload_result['upload_url'], file_content, file.content_type)
 
 
 
615
  if not upload_success:
616
- raise HTTPException(status_code=500, detail="File upload failed after multiple attempts")
617
 
618
  original_url = upload_result['serving_url']
619
  mirrored_url = f"/rbxg/{original_url.split('/pbxt/')[1]}"
620
 
 
 
 
621
  return JSONResponse(content={"url": mirrored_url})
622
 
623
  @app.get("/rbxg/{path:path}")
@@ -633,7 +1204,7 @@ async def handle_video_stream(path: str, request: Request):
633
  yield chunk
634
 
635
  headers = dict(response.headers)
636
- headers['Access-Control-Allow-Origin'] = '*'
637
  headers['Content-Disposition'] = 'inline'
638
 
639
  if response.status_code == 206:
@@ -700,7 +1271,7 @@ async def initiate_upload(cookies: Dict[str, str], filename: str, content_type:
700
  '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',
701
  'Referer': 'https://replicate.com/levelsio/neon-tokyo',
702
  'Origin': 'https://replicate.com',
703
- 'Accept': '*/*',
704
  'Accept-Language': 'en-US,en;q=0.5',
705
  'Accept-Encoding': 'identity',
706
  'Sec-Fetch-Dest': 'empty',
@@ -729,12 +1300,12 @@ async def retry_upload(upload_url: str, file_content: bytes, content_type: str,
729
  success = await upload_file(upload_url, file_content, content_type)
730
  if success:
731
  return True
732
- print(f"Upload attempt {retries + 1} failed. Retrying...")
733
  except Exception as e:
734
- print(f"Error during upload attempt {retries + 1}: {e}")
735
 
736
- retries += 1
737
  await asyncio.sleep(delay)
738
  delay = min(delay * 2, 60) # Exponential backoff, capped at 60 seconds
739
-
 
740
  return False
 
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
 
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>
 
743
  margin-top: 10px;
744
  }
745
 
746
+ /* File Types /
747
  .file-types {
748
  margin-top: 2rem;
749
  font-size: 0.8rem;
 
845
  <h1>Radd PRO Uploader</h1>
846
  <form id="uploadForm">
847
  <div id="dropZone" class="drop-zone">
848
+ <input type="file" name="file" id="file" class="file-input" accept=".zip,.mp4,.txt,.mp3,image/,.pdf" required>
849
  <label for="file" class="btn">Choose File</label>
850
  <p>or drag and drop file here/paste image</p>
851
  </div>
 
862
 
863
  <div id="embedModal" class="modal">
864
  <div class="modal-content">
865
+ <span class="close">×</span>
866
  <h2>Embed Video Link</h2>
867
  <p>copy the link to embed it on discord:</p>
868
  <div class="embed-container">
 
879
  const progressContainer = document.getElementById('progressContainer');
880
  const loadingSpinner = document.getElementById('loadingSpinner');
881
  const resultContainer = document.getElementById('resultContainer');
882
+ const dropZone = document.getElementById('dropZone');
883
  const modal = document.getElementById('embedModal');
884
  const span = document.getElementsByClassName("close")[0];
885
  const embedLinkInput = document.getElementById('embedLink');
 
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
 
 
943
  }
944
 
945
  async function uploadFile(file) {
946
+ const chunkSize = 1024 * 1024; // 1 MB
947
+ const totalChunks = Math.ceil(file.size / chunkSize);
948
+ const uploadId = generateUploadId();
949
  progressContainer.innerHTML = '';
950
  progressContainer.style.display = 'block';
951
  loadingSpinner.style.display = 'block';
 
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);
969
+ formData.append('totalChunks', totalChunks);
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);
979
+ success = true;
980
+ } catch (error) {
981
+ console.error('Chunk upload error:', error);
982
+ // Wait for a short time before retrying
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);
 
 
 
 
 
 
 
1000
  resetUploadState();
1001
+ } else {
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';
 
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
 
 
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
  }
 
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;
1104
+ const totalProgress = ((start + event.loaded) / totalSize) * 100;
1105
+ progressBar.style.width = totalProgress + '%';
1106
+ }
1107
+ };
1108
+
1109
+ xhr.onload = function() {
1110
+ if (xhr.status === 200) {
1111
+ resolve();
1112
+ } else {
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
+ }
1124
  </script>
1125
  </body>
1126
  </html>
 
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()
1136
+ chunk = form['file'].file
1137
+ chunk_index = int(form['chunkIndex'])
1138
+ total_chunks = int(form['totalChunks'])
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}")
1146
+ with open(chunk_path, 'wb') as f:
1147
+ while True:
1148
+ data = chunk.read(1024 * 1024)
1149
+ if not data:
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}")
 
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:
 
1271
  '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',
1272
  'Referer': 'https://replicate.com/levelsio/neon-tokyo',
1273
  'Origin': 'https://replicate.com',
1274
+ 'Accept': '/*',
1275
  'Accept-Language': 'en-US,en;q=0.5',
1276
  'Accept-Encoding': 'identity',
1277
  'Sec-Fetch-Dest': 'empty',
 
1300
  success = await upload_file(upload_url, file_content, content_type)
1301
  if success:
1302
  return True
1303
+ print("Upload failed. Retrying...")
1304
  except Exception as e:
1305
+ print(f"Error during upload: {e}")
1306
 
 
1307
  await asyncio.sleep(delay)
1308
  delay = min(delay * 2, 60) # Exponential backoff, capped at 60 seconds
1309
+ retries += 1
1310
+
1311
  return False