coollsd commited on
Commit
cec6e17
1 Parent(s): ff87641

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +123 -81
app.py CHANGED
@@ -2,9 +2,11 @@ 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
 
@@ -385,7 +387,7 @@ HTML_CONTENT = """
385
  <div class="loading-spinner" id="loadingSpinner"></div>
386
  </form>
387
  <div class="result-container" id="resultContainer"></div>
388
- <div class="file-types">
389
  Allowed file types: .zip, .mp4, .txt, .mp3, all image types, .pdf
390
  </div>
391
  </div>
@@ -409,7 +411,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,14 +419,14 @@ 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
 
427
- dropZone.addEventListener('dragover', (e) => {
428
  e.preventDefault();
429
  dropZone.classList.add('drag-over');
430
  });
@@ -486,43 +488,47 @@ HTML_CONTENT = """
486
  const formData = new FormData();
487
  formData.append('file', file);
488
 
489
- let uploadedBytes = 0;
490
- const chunkSize = 1024 * 1024; // 1MB chunks
491
- const totalSize = file.size;
492
-
493
- while (uploadedBytes < totalSize) {
494
- const chunk = file.slice(uploadedBytes, uploadedBytes + chunkSize);
495
- formData.set('file', chunk, file.name);
496
-
497
  try {
498
- const response = await fetch('/upload', {
499
- method: 'POST',
500
- body: formData,
501
- headers: {
502
- 'Content-Range': `bytes ${uploadedBytes}-${uploadedBytes + chunk.size - 1}/${totalSize}`
 
 
 
 
 
 
 
 
 
 
 
503
  }
504
- });
505
 
506
- if (!response.ok) {
507
- throw new Error(`HTTP error! status: ${response.status}`);
508
- }
509
 
510
- const result = await response.json();
511
- if (result.url) {
512
- addResultLink(result.url, file.name);
513
- break;
514
- }
515
 
516
- uploadedBytes += chunk.size;
517
- updateProgress(uploadedBytes, totalSize, progressBar.querySelector('.progress'));
 
 
 
 
 
518
  } catch (error) {
519
  console.error('Upload error:', error);
520
- await new Promise(resolve => setTimeout(resolve, 1000)); // Wait 1 second before retrying
521
- // The loop will continue from where it left off
 
522
  }
523
  }
524
-
525
- resetUploadState();
526
  }
527
 
528
  function createProgressBar(fileName) {
@@ -541,9 +547,11 @@ HTML_CONTENT = """
541
  return container;
542
  }
543
 
544
- function updateProgress(uploadedBytes, totalBytes, progressBar) {
545
- const percentComplete = (uploadedBytes / totalBytes) * 100;
546
- progressBar.style.width = percentComplete + '%';
 
 
547
  }
548
 
549
  function resetUploadState() {
@@ -621,37 +629,85 @@ async def handle_upload(request: Request, file: UploadFile = File(...)):
621
  if not content_range:
622
  raise HTTPException(status_code=400, detail="Content-Range header is missing")
623
 
624
- _, range_str = content_range.split(' ')
625
- start_byte, end_byte, total_size = map(int, range_str.replace('bytes', '').split('-')[0].split('/'))
 
 
 
 
 
 
 
 
 
626
 
627
  if not file:
628
  raise HTTPException(status_code=400, detail="No file part")
629
  if file.filename == '':
630
  raise HTTPException(status_code=400, detail="No selected file")
631
 
632
- cookies = await get_cookies()
633
- if 'csrftoken' not in cookies or 'sessionid' not in cookies:
634
- raise HTTPException(status_code=500, detail="Failed to obtain necessary cookies")
 
 
 
 
 
 
635
 
636
  if start_byte == 0:
 
 
 
 
 
 
637
  upload_result = await initiate_upload(cookies, file.filename, file.content_type)
638
  if not upload_result or 'upload_url' not in upload_result:
639
  raise HTTPException(status_code=500, detail="Failed to initiate upload")
 
 
 
 
640
  else:
641
- upload_result = {'upload_url': file.filename} # Use filename as a key to retrieve the upload URL
 
 
 
 
642
 
 
643
  file_content = await file.read()
644
- upload_success = await upload_chunk(upload_result['upload_url'], file_content, start_byte, end_byte, total_size)
645
- if not upload_success:
646
- raise HTTPException(status_code=500, detail="Chunk upload failed")
647
 
648
- if end_byte + 1 >= total_size:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
649
  original_url = upload_result.get('serving_url')
650
  if original_url:
651
  mirrored_url = f"/rbxg/{original_url.split('/pbxt/')[1]}"
 
 
 
652
  return JSONResponse(content={"url": mirrored_url})
653
-
654
- return JSONResponse(content={"status": "chunk uploaded"})
 
 
655
 
656
  @app.get("/rbxg/{path:path}")
657
  async def handle_video_stream(path: str, request: Request):
@@ -718,7 +774,7 @@ async def embed_video(url: str, thumbnail: str):
718
  async def get_cookies() -> Dict[str, str]:
719
  try:
720
  response = requests.get('https://replicate.com/levelsio/neon-tokyo', headers={
721
- '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'
722
  })
723
  return dict(response.cookies)
724
  except Exception as e:
@@ -728,9 +784,9 @@ async def get_cookies() -> Dict[str, str]:
728
  async def initiate_upload(cookies: Dict[str, str], filename: str, content_type: str) -> Dict:
729
  url = f'https://replicate.com/api/upload/{filename}?content_type={content_type}'
730
  try:
731
- response = requests.post(url, cookies=cookies, headers={
732
  'X-CSRFToken': cookies.get('csrftoken'),
733
- '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',
734
  'Referer': 'https://replicate.com/levelsio/neon-tokyo',
735
  'Origin': 'https://replicate.com',
736
  'Accept': '*/*',
@@ -738,48 +794,34 @@ async def initiate_upload(cookies: Dict[str, str], filename: str, content_type:
738
  'Accept-Encoding': 'identity',
739
  'Sec-Fetch-Dest': 'empty',
740
  'Sec-Fetch-Mode': 'cors',
741
- 'Sec-Fetch-Site': 'same-origin',
742
- 'Sec-GPC': '1',
743
- 'Priority': 'u=1, i'
744
- })
745
  return response.json()
746
  except Exception as e:
747
  print(f'Error initiating upload: {e}')
748
  raise
749
 
750
- async def upload_chunk(upload_url: str, chunk_content: bytes, start_byte: int, end_byte: int, total_size: int) -> bool:
751
  try:
752
  headers = {
753
- 'Content-Type': 'application/octet-stream',
754
- 'Content-Range': f'bytes {start_byte}-{end_byte}/{total_size}'
755
  }
756
- response = requests.put(upload_url, data=chunk_content, headers=headers)
757
  return response.status_code in [200, 201, 204]
758
  except Exception as e:
759
- print(f'Error uploading chunk: {e}')
760
  return False
761
 
762
  async def retry_upload(upload_url: str, file_content: bytes, content_type: str, max_retries: int = 5, delay: int = 1) -> bool:
763
  retries = 0
764
  while retries < max_retries:
765
- try:
766
- success = await upload_file(upload_url, file_content, content_type)
767
- if success:
768
- return True
769
  print(f"Upload attempt {retries + 1} failed. Retrying...")
770
- except Exception as e:
771
- print(f"Error during upload attempt {retries + 1}: {e}")
772
-
773
- retries += 1
774
- await asyncio.sleep(delay)
775
- delay = min(delay * 2, 60) # Exponential backoff, capped at 60 seconds
776
-
777
- return False
778
-
779
- async def upload_file(upload_url: str, file_content: bytes, content_type: str) -> bool:
780
- try:
781
- response = requests.put(upload_url, data=file_content, headers={'Content-Type': content_type})
782
- return response.status_code == 200
783
- except Exception as e:
784
- print(f'Error uploading file: {e}')
785
- return False
 
2
  from fastapi.responses import HTMLResponse, JSONResponse, StreamingResponse
3
  from fastapi.middleware.cors import CORSMiddleware
4
  import requests
 
5
  import asyncio
6
  from typing import Dict
7
+ import os
8
+ import json
9
+ import re
10
 
11
  app = FastAPI()
12
 
 
387
  <div class="loading-spinner" id="loadingSpinner"></div>
388
  </form>
389
  <div class="result-container" id="resultContainer"></div>
390
+ <div class="file-types">
391
  Allowed file types: .zip, .mp4, .txt, .mp3, all image types, .pdf
392
  </div>
393
  </div>
 
411
  const progressContainer = document.getElementById('progressContainer');
412
  const loadingSpinner = document.getElementById('loadingSpinner');
413
  const resultContainer = document.getElementById('resultContainer');
414
+ const dropZone = document.getElementById('dropZone');
415
  const modal = document.getElementById('embedModal');
416
  const span = document.getElementsByClassName("close")[0];
417
  const embedLinkInput = document.getElementById('embedLink');
 
419
 
420
  fileInput.addEventListener('change', handleFileSelect);
421
 
422
+ uploadForm.addEventListener('submit', (e) => {
423
  e.preventDefault();
424
  if (fileInput.files.length > 0) {
425
+ uploadFile(fileInput.files[0]);
426
  }
427
  });
428
 
429
+ dropZone.addEventListener('dragover', (e) => {
430
  e.preventDefault();
431
  dropZone.classList.add('drag-over');
432
  });
 
488
  const formData = new FormData();
489
  formData.append('file', file);
490
 
491
+ while (true) {
 
 
 
 
 
 
 
492
  try {
493
+ const xhr = new XMLHttpRequest();
494
+ xhr.open('POST', '/upload', true);
495
+ xhr.upload.onprogress = (event) => updateProgress(event, progressBar.querySelector('.progress'));
496
+
497
+ xhr.onload = function() {
498
+ if (xhr.status === 200) {
499
+ const response = JSON.parse(xhr.responseText);
500
+ if (response.url) {
501
+ addResultLink(response.url, file.name);
502
+ resetUploadState();
503
+ return;
504
+ } else {
505
+ throw new Error('Upload failed: ' + response.error);
506
+ }
507
+ } else {
508
+ throw new Error(`HTTP error! status: ${xhr.status}`);
509
  }
510
+ };
511
 
512
+ xhr.onerror = function() {
513
+ throw new Error('Network error occurred');
514
+ };
515
 
516
+ xhr.send(formData);
 
 
 
 
517
 
518
+ // Wait for the request to complete
519
+ await new Promise((resolve, reject) => {
520
+ xhr.onloadend = resolve;
521
+ xhr.onerror = reject;
522
+ });
523
+
524
+ break; // Success, exit the loop
525
  } catch (error) {
526
  console.error('Upload error:', error);
527
+ // Wait for a short time before retrying
528
+ await new Promise(resolve => setTimeout(resolve, 1000));
529
+ // The loop will continue, retrying the upload
530
  }
531
  }
 
 
532
  }
533
 
534
  function createProgressBar(fileName) {
 
547
  return container;
548
  }
549
 
550
+ function updateProgress(event, progressBar) {
551
+ if (event.lengthComputable) {
552
+ const percentComplete = (event.loaded / event.total) * 100;
553
+ progressBar.style.width = percentComplete + '%';
554
+ }
555
  }
556
 
557
  function resetUploadState() {
 
629
  if not content_range:
630
  raise HTTPException(status_code=400, detail="Content-Range header is missing")
631
 
632
+ # Parse Content-Range header
633
+ content_range_match = re.match(r'bytes (\d+)-(\d+)/(\d+)', content_range)
634
+ if not content_range_match:
635
+ raise HTTPException(status_code=400, detail="Invalid Content-Range header format")
636
+
637
+ start_byte = int(content_range_match.group(1))
638
+ end_byte = int(content_range_match.group(2))
639
+ total_size = int(content_range_match.group(3))
640
+
641
+ if start_byte > end_byte or end_byte >= total_size:
642
+ raise HTTPException(status_code=400, detail="Invalid Content-Range header values")
643
 
644
  if not file:
645
  raise HTTPException(status_code=400, detail="No file part")
646
  if file.filename == '':
647
  raise HTTPException(status_code=400, detail="No selected file")
648
 
649
+ # Create temporary directory to store the chunks if it doesn't exist
650
+ upload_dir = os.path.join('/tmp', 'uploads')
651
+ os.makedirs(upload_dir, exist_ok=True)
652
+
653
+ # Temporary file path
654
+ temp_file_path = os.path.join(upload_dir, file.filename)
655
+
656
+ # Path to store upload metadata
657
+ meta_file_path = temp_file_path + '.json'
658
 
659
  if start_byte == 0:
660
+ # Start of a new upload
661
+ cookies = await get_cookies()
662
+ if 'csrftoken' not in cookies or 'sessionid' not in cookies:
663
+ raise HTTPException(status_code=500, detail="Failed to obtain necessary cookies")
664
+
665
+ # Initiate the upload
666
  upload_result = await initiate_upload(cookies, file.filename, file.content_type)
667
  if not upload_result or 'upload_url' not in upload_result:
668
  raise HTTPException(status_code=500, detail="Failed to initiate upload")
669
+
670
+ # Save the upload_url and serving_url to a metadata file
671
+ with open(meta_file_path, 'w') as meta_file:
672
+ json.dump(upload_result, meta_file)
673
  else:
674
+ # For subsequent chunks, read the metadata file to get upload_url and serving_url
675
+ if not os.path.exists(meta_file_path):
676
+ raise HTTPException(status_code=400, detail="Upload metadata not found")
677
+ with open(meta_file_path, 'r') as meta_file:
678
+ upload_result = json.load(meta_file)
679
 
680
+ # Read the chunk content
681
  file_content = await file.read()
 
 
 
682
 
683
+ # Write the chunk to the temporary file at the correct offset
684
+ with open(temp_file_path, 'ab') as f:
685
+ f.seek(start_byte)
686
+ f.write(file_content)
687
+
688
+ # Check if the upload is complete
689
+ file_size = os.path.getsize(temp_file_path)
690
+ if file_size == total_size:
691
+ # Read the entire file content
692
+ with open(temp_file_path, 'rb') as f:
693
+ full_file_content = f.read()
694
+
695
+ # Upload the file to Replicate
696
+ upload_success = await retry_upload(upload_result['upload_url'], full_file_content, file.content_type)
697
+ if not upload_success:
698
+ raise HTTPException(status_code=500, detail="Failed to upload file to Replicate")
699
+
700
  original_url = upload_result.get('serving_url')
701
  if original_url:
702
  mirrored_url = f"/rbxg/{original_url.split('/pbxt/')[1]}"
703
+ # Clean up the temporary files
704
+ os.remove(temp_file_path)
705
+ os.remove(meta_file_path)
706
  return JSONResponse(content={"url": mirrored_url})
707
+ else:
708
+ raise HTTPException(status_code=500, detail="serving_url not found")
709
+ else:
710
+ return JSONResponse(content={"status": "chunk uploaded"})
711
 
712
  @app.get("/rbxg/{path:path}")
713
  async def handle_video_stream(path: str, request: Request):
 
774
  async def get_cookies() -> Dict[str, str]:
775
  try:
776
  response = requests.get('https://replicate.com/levelsio/neon-tokyo', headers={
777
+ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)'
778
  })
779
  return dict(response.cookies)
780
  except Exception as e:
 
784
  async def initiate_upload(cookies: Dict[str, str], filename: str, content_type: str) -> Dict:
785
  url = f'https://replicate.com/api/upload/{filename}?content_type={content_type}'
786
  try:
787
+ headers = {
788
  'X-CSRFToken': cookies.get('csrftoken'),
789
+ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)',
790
  'Referer': 'https://replicate.com/levelsio/neon-tokyo',
791
  'Origin': 'https://replicate.com',
792
  'Accept': '*/*',
 
794
  'Accept-Encoding': 'identity',
795
  'Sec-Fetch-Dest': 'empty',
796
  'Sec-Fetch-Mode': 'cors',
797
+ 'Sec-Fetch-Site': 'same-origin'
798
+ }
799
+ response = requests.post(url, cookies=cookies, headers=headers)
 
800
  return response.json()
801
  except Exception as e:
802
  print(f'Error initiating upload: {e}')
803
  raise
804
 
805
+ async def upload_file(upload_url: str, file_content: bytes, content_type: str) -> bool:
806
  try:
807
  headers = {
808
+ 'Content-Type': content_type
 
809
  }
810
+ response = requests.put(upload_url, data=file_content, headers=headers)
811
  return response.status_code in [200, 201, 204]
812
  except Exception as e:
813
+ print(f'Error uploading file: {e}')
814
  return False
815
 
816
  async def retry_upload(upload_url: str, file_content: bytes, content_type: str, max_retries: int = 5, delay: int = 1) -> bool:
817
  retries = 0
818
  while retries < max_retries:
819
+ success = await upload_file(upload_url, file_content, content_type)
820
+ if success:
821
+ return True
822
+ else:
823
  print(f"Upload attempt {retries + 1} failed. Retrying...")
824
+ retries += 1
825
+ await asyncio.sleep(delay)
826
+ delay = min(delay * 2, 60) # Exponential backoff
827
+ return False