coollsd commited on
Commit
d3ff30a
1 Parent(s): ca5c2d7

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +106 -162
app.py CHANGED
@@ -3,7 +3,9 @@ from fastapi.responses import HTMLResponse, JSONResponse, StreamingResponse
3
  import requests
4
  import time
5
  import asyncio
6
- from typing import Dict
 
 
7
 
8
  app = FastAPI()
9
 
@@ -418,7 +420,7 @@ HTML_CONTENT = """
418
  overflow: auto;
419
  }
420
 
421
- .quick-open-content {
422
  margin: 5% auto;
423
  padding: 20px;
424
  width: 90%;
@@ -440,7 +442,7 @@ HTML_CONTENT = """
440
  border: none;
441
  }
442
 
443
- @media (max-width: 480px) {
444
  .container {
445
  padding: 1.5rem;
446
  }
@@ -518,12 +520,12 @@ HTML_CONTENT = """
518
  <h1>Radd PRO Uploader</h1>
519
  <form id="uploadForm">
520
  <div id="dropZone" class="drop-zone">
521
- <input type="file" name="file" id="file" class="file-input" accept=".zip,.mp4,.txt,.mp3,image/*,.pdf" required>
522
- <label for="file" class="btn">Choose File</label>
523
- <p>or drag and drop file here/paste image</p>
524
  </div>
525
  <div class="file-name" id="fileName"></div>
526
- <button type="submit" id="uploadBtn" class="btn" style="display: none; margin-top: 1rem;">Upload File</button>
527
  <div class="progress-container" id="progressContainer"></div>
528
  <div class="loading-spinner" id="loadingSpinner"></div>
529
  </form>
@@ -562,7 +564,7 @@ HTML_CONTENT = """
562
  </div>
563
 
564
  <script>
565
- const fileInput = document.getElementById('file');
566
  const fileName = document.getElementById('fileName');
567
  const uploadForm = document.getElementById('uploadForm');
568
  const progressContainer = document.getElementById('progressContainer');
@@ -584,7 +586,7 @@ HTML_CONTENT = """
584
  uploadForm.addEventListener('submit', (e) => {
585
  e.preventDefault();
586
  if (fileInput.files.length > 0) {
587
- uploadFile(fileInput.files[0]);
588
  }
589
  });
590
 
@@ -644,17 +646,13 @@ HTML_CONTENT = """
644
 
645
  function handleFileSelect(e) {
646
  if (e.target.files && e.target.files.length > 0) {
647
- const file = e.target.files[0];
648
- fileName.textContent = file.name;
649
  uploadBtn.style.display = 'inline-block';
650
-
651
- const dataTransfer = new DataTransfer();
652
- dataTransfer.items.add(file);
653
- fileInput.files = dataTransfer.files;
654
  }
655
  }
656
 
657
- async function uploadFile(file) {
658
  progressContainer.innerHTML = '';
659
  progressContainer.style.display = 'block';
660
  loadingSpinner.style.display = 'block';
@@ -662,73 +660,32 @@ HTML_CONTENT = """
662
  resultContainer.innerHTML = '';
663
  resultContainer.style.display = 'none';
664
 
665
- const progressBar = createProgressBar(file.name);
666
- progressContainer.appendChild(progressBar);
667
-
668
  const formData = new FormData();
669
- formData.append('file', file);
670
-
671
- while (true) {
672
- try {
673
- const xhr = new XMLHttpRequest();
674
- xhr.open('POST', '/upload', true);
675
- xhr.upload.onprogress = (event) => updateProgress(event, progressBar.querySelector('.progress'));
676
-
677
- xhr.onload = function() {
678
- if (xhr.status === 200) {
679
- const response = JSON.parse(xhr.responseText);
680
- if (response.url) {
681
- addResultLink(response.url, file.name);
682
- saveToHistory(file.name, response.url);
683
- resetUploadState();
684
- return;
685
- } else {
686
- throw new Error('Upload failed: ' + response.error);
687
- }
688
- } else {
689
- throw new Error(`HTTP error! status: ${xhr.status}`);
690
- }
691
- };
692
-
693
- xhr.onerror = function() {
694
- throw new Error('Network error occurred');
695
- };
696
-
697
- xhr.send(formData);
698
-
699
- await new Promise((resolve, reject) => {
700
- xhr.onloadend = resolve;
701
- xhr.onerror = reject;
702
- });
703
-
704
- break;
705
- } catch (error) {
706
- console.error('Upload error:', error);
707
- await new Promise(resolve => setTimeout(resolve, 1000));
708
- }
709
  }
710
- }
711
 
712
- function createProgressBar(fileName) {
713
- const progressBarContainer = document.createElement('div');
714
- progressBarContainer.className = 'progress-bar';
715
- const progress = document.createElement('div');
716
- progress.className = 'progress';
717
- progressBarContainer.appendChild(progress);
718
- const label = document.createElement('div');
719
- label.textContent = fileName;
720
- label.style.fontSize = '0.8rem';
721
- label.style.marginBottom = '5px';
722
- const container = document.createElement('div');
723
- container.appendChild(label);
724
- container.appendChild(progressBarContainer);
725
- return container;
726
- }
727
-
728
- function updateProgress(event, progressBar) {
729
- if (event.lengthComputable) {
730
- const percentComplete = (event.loaded / event.total) * 100;
731
- progressBar.style.width = percentComplete + '%';
 
732
  }
733
  }
734
 
@@ -738,47 +695,46 @@ HTML_CONTENT = """
738
  uploadBtn.style.display = 'none';
739
  uploadBtn.disabled = false;
740
  loadingSpinner.style.display = 'none';
741
- }
742
-
743
- function addResultLink(url, fileName) {
744
- const linkContainer = document.createElement('div');
745
- linkContainer.style.marginBottom = '10px';
746
-
747
- const link = document.createElement('a');
748
- link.href = url;
749
- link.textContent = `View ${fileName}`;
750
- link.className = 'result-link';
751
- link.target = '_blank';
752
-
753
- linkContainer.appendChild(link);
754
-
755
- const buttonsContainer = document.createElement('div');
756
- buttonsContainer.className = 'link-buttons';
757
-
758
- const copyBtn = document.createElement('button');
759
- copyBtn.textContent = 'Copy Link';
760
- copyBtn.className = 'small-btn copy-btn';
761
- copyBtn.onclick = () => {
762
- navigator.clipboard.writeText(window.location.origin + url).then(() => {
763
- alert('Link copied to clipboard!');
764
  });
765
  };
766
- buttonsContainer.appendChild(copyBtn);
767
-
768
- if (fileName.toLowerCase().endsWith('.mp4')) {
769
- const embedBtn = document.createElement('button');
770
- embedBtn.textContent = 'Embed Video for Discord';
771
- embedBtn.className = 'small-btn embed-btn';
772
- embedBtn.onclick = () => {
773
- showEmbedModal(url);
774
- };
775
- buttonsContainer.appendChild(embedBtn);
776
- }
777
-
778
- linkContainer.appendChild(buttonsContainer);
779
 
780
- resultContainer.appendChild(linkContainer);
781
  resultContainer.style.display = 'block';
 
 
782
  }
783
 
784
  function showEmbedModal(url) {
@@ -793,9 +749,9 @@ HTML_CONTENT = """
793
  alert('Embed link copied to clipboard!');
794
  }
795
 
796
- function saveToHistory(fileName, url) {
797
  let history = JSON.parse(localStorage.getItem('uploadHistory')) || [];
798
- history.unshift({ fileName, url, timestamp: new Date().toISOString() });
799
  if (history.length > 500) history = history.slice(0, 500);
800
  localStorage.setItem('uploadHistory', JSON.stringify(history));
801
  }
@@ -809,56 +765,37 @@ HTML_CONTENT = """
809
 
810
  const itemName = document.createElement('span');
811
  itemName.className = 'history-item-name';
812
- itemName.textContent = item.fileName;
813
  historyItem.appendChild(itemName);
814
 
815
  const actionsContainer = document.createElement('div');
816
  actionsContainer.className = 'history-item-actions';
817
 
818
  const copyBtn = document.createElement('button');
819
- copyBtn.textContent = 'Copy Link';
820
  copyBtn.className = 'small-btn';
821
  copyBtn.onclick = () => {
822
- navigator.clipboard.writeText(window.location.origin + item.url).then(() => {
823
- alert('Link copied to clipboard!');
824
  });
825
  };
826
  actionsContainer.appendChild(copyBtn);
827
 
828
  const openBtn = document.createElement('button');
829
- openBtn.textContent = 'Open';
830
  openBtn.className = 'small-btn';
831
  openBtn.onclick = () => {
832
- window.open(window.location.origin + item.url, '_blank');
833
  };
834
  actionsContainer.appendChild(openBtn);
835
 
836
- const quickOpenBtn = document.createElement('button');
837
- quickOpenBtn.textContent = 'Quick Open';
838
- quickOpenBtn.className = 'small-btn';
839
- quickOpenBtn.onclick = () => {
840
- quickOpen(item.url, item.fileName);
841
- };
842
- actionsContainer.appendChild(quickOpenBtn);
843
-
844
- if (item.fileName.toLowerCase().endsWith('.mp4')) {
845
- const embedBtn = document.createElement('button');
846
- embedBtn.textContent = 'Embed';
847
- embedBtn.className = 'small-btn';
848
- embedBtn.onclick = () => {
849
- showEmbedModal(item.url);
850
- historyModal.style.display = "none";
851
- };
852
- actionsContainer.appendChild(embedBtn);
853
- }
854
-
855
  historyItem.appendChild(actionsContainer);
856
  historyList.appendChild(historyItem);
857
  });
858
  historyModal.style.display = "block";
859
  }
860
 
861
- function quickOpen(url, fileName) {
862
  quickOpenContent.innerHTML = '';
863
  const fullUrl = window.location.origin + url;
864
 
@@ -909,31 +846,38 @@ async def index():
909
  return HTML_CONTENT
910
 
911
  @app.post("/upload")
912
- async def handle_upload(file: UploadFile = File(...)):
913
- if not file.filename:
914
- return JSONResponse(content={"error": "No file selected."}, status_code=400)
915
 
916
  cookies = await get_cookies()
917
  if 'csrftoken' not in cookies or 'sessionid' not in cookies:
918
  return JSONResponse(content={"error": "Failed to obtain necessary cookies"}, status_code=500)
919
 
920
- upload_result = await initiate_upload(cookies, file.filename, file.content_type)
921
- if not upload_result or 'upload_url' not in upload_result:
922
- return JSONResponse(content={"error": "Failed to initiate upload"}, status_code=500)
 
 
 
 
 
 
923
 
924
- file_content = await file.read()
925
- upload_success = await retry_upload(upload_result['upload_url'], file_content, file.content_type)
926
- if not upload_success:
927
- return JSONResponse(content={"error": "File upload failed after multiple attempts"}, status_code=500)
928
 
929
- original_url = upload_result['serving_url']
930
- mirrored_url = f"/rbxg/{original_url.split('/pbxt/')[1]}"
 
931
 
932
- return JSONResponse(content={"url": mirrored_url})
933
 
934
- @app.get("/rbxg/{path:path}")
935
- async def handle_video_stream(path: str, request: Request):
936
- original_url = f'https://replicate.delivery/pbxt/{path}'
937
  range_header = request.headers.get('Range')
938
 
939
  headers = {'Range': range_header} if range_header else {}
@@ -1046,4 +990,4 @@ async def retry_upload(upload_url: str, file_content: bytes, content_type: str,
1046
  await asyncio.sleep(delay)
1047
  delay = min(delay * 2, 60) # Exponential backoff, capped at 60 seconds
1048
 
1049
- return False
 
3
  import requests
4
  import time
5
  import asyncio
6
+ from typing import Dict, List
7
+ import uuid
8
+ import os
9
 
10
  app = FastAPI()
11
 
 
420
  overflow: auto;
421
  }
422
 
423
+ .quick-open-content {
424
  margin: 5% auto;
425
  padding: 20px;
426
  width: 90%;
 
442
  border: none;
443
  }
444
 
445
+ @media (max-width: 480px) {
446
  .container {
447
  padding: 1.5rem;
448
  }
 
520
  <h1>Radd PRO Uploader</h1>
521
  <form id="uploadForm">
522
  <div id="dropZone" class="drop-zone">
523
+ <input type="file" name="files" id="files" class="file-input" accept=".zip,.mp4,.txt,.mp3,image/*,.pdf" required multiple>
524
+ <label for="files" class="btn">Choose Files</label>
525
+ <p>or drag and drop files here/paste images</p>
526
  </div>
527
  <div class="file-name" id="fileName"></div>
528
+ <button type="submit" id="uploadBtn" class="btn" style="display: none; margin-top: 1rem;">Upload Files</button>
529
  <div class="progress-container" id="progressContainer"></div>
530
  <div class="loading-spinner" id="loadingSpinner"></div>
531
  </form>
 
564
  </div>
565
 
566
  <script>
567
+ const fileInput = document.getElementById('files');
568
  const fileName = document.getElementById('fileName');
569
  const uploadForm = document.getElementById('uploadForm');
570
  const progressContainer = document.getElementById('progressContainer');
 
586
  uploadForm.addEventListener('submit', (e) => {
587
  e.preventDefault();
588
  if (fileInput.files.length > 0) {
589
+ uploadFiles(fileInput.files);
590
  }
591
  });
592
 
 
646
 
647
  function handleFileSelect(e) {
648
  if (e.target.files && e.target.files.length > 0) {
649
+ const fileNames = Array.from(e.target.files).map(file => file.name).join(', ');
650
+ fileName.textContent = fileNames;
651
  uploadBtn.style.display = 'inline-block';
 
 
 
 
652
  }
653
  }
654
 
655
+ async function uploadFiles(files) {
656
  progressContainer.innerHTML = '';
657
  progressContainer.style.display = 'block';
658
  loadingSpinner.style.display = 'block';
 
660
  resultContainer.innerHTML = '';
661
  resultContainer.style.display = 'none';
662
 
 
 
 
663
  const formData = new FormData();
664
+ for (let i = 0; i < files.length; i++) {
665
+ formData.append('files', files[i]);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
666
  }
 
667
 
668
+ try {
669
+ const response = await fetch('/upload', {
670
+ method: 'POST',
671
+ body: formData
672
+ });
673
+
674
+ if (response.ok) {
675
+ const result = await response.json();
676
+ if (result.folder_url) {
677
+ addFolderResult(result.folder_url, result.files);
678
+ } else {
679
+ throw new Error('Upload failed: No folder URL received');
680
+ }
681
+ } else {
682
+ throw new Error(`HTTP error! status: ${response.status}`);
683
+ }
684
+ } catch (error) {
685
+ console.error('Upload error:', error);
686
+ alert('Upload failed. Please try again.');
687
+ } finally {
688
+ resetUploadState();
689
  }
690
  }
691
 
 
695
  uploadBtn.style.display = 'none';
696
  uploadBtn.disabled = false;
697
  loadingSpinner.style.display = 'none';
698
+ progressContainer.style.display = 'none';
699
+ }
700
+
701
+ function addFolderResult(folderUrl, files) {
702
+ const folderContainer = document.createElement('div');
703
+ folderContainer.className = 'folder-result';
704
+
705
+ const folderLink = document.createElement('a');
706
+ folderLink.href = folderUrl;
707
+ folderLink.textContent = 'View Folder';
708
+ folderLink.className = 'result-link';
709
+ folderLink.target = '_blank';
710
+ folderContainer.appendChild(folderLink);
711
+
712
+ const copyFolderBtn = document.createElement('button');
713
+ copyFolderBtn.textContent = 'Copy Folder Link';
714
+ copyFolderBtn.className = 'small-btn';
715
+ copyFolderBtn.onclick = () => {
716
+ navigator.clipboard.writeText(window.location.origin + folderUrl).then(() => {
717
+ alert('Folder link copied to clipboard!');
 
 
 
718
  });
719
  };
720
+ folderContainer.appendChild(copyFolderBtn);
721
+
722
+ const fileList = document.createElement('ul');
723
+ files.forEach(file => {
724
+ const listItem = document.createElement('li');
725
+ const fileLink = document.createElement('a');
726
+ fileLink.href = file.url;
727
+ fileLink.textContent = file.name;
728
+ fileLink.target = '_blank';
729
+ listItem.appendChild(fileLink);
730
+ fileList.appendChild(listItem);
731
+ });
732
+ folderContainer.appendChild(fileList);
733
 
734
+ resultContainer.appendChild(folderContainer);
735
  resultContainer.style.display = 'block';
736
+
737
+ saveToHistory(folderUrl, files);
738
  }
739
 
740
  function showEmbedModal(url) {
 
749
  alert('Embed link copied to clipboard!');
750
  }
751
 
752
+ function saveToHistory(folderUrl, files) {
753
  let history = JSON.parse(localStorage.getItem('uploadHistory')) || [];
754
+ history.unshift({ folderUrl, files, timestamp: new Date().toISOString() });
755
  if (history.length > 500) history = history.slice(0, 500);
756
  localStorage.setItem('uploadHistory', JSON.stringify(history));
757
  }
 
765
 
766
  const itemName = document.createElement('span');
767
  itemName.className = 'history-item-name';
768
+ itemName.textContent = `Folder: ${new Date(item.timestamp).toLocaleString()}`;
769
  historyItem.appendChild(itemName);
770
 
771
  const actionsContainer = document.createElement('div');
772
  actionsContainer.className = 'history-item-actions';
773
 
774
  const copyBtn = document.createElement('button');
775
+ copyBtn.textContent = 'Copy Folder Link';
776
  copyBtn.className = 'small-btn';
777
  copyBtn.onclick = () => {
778
+ navigator.clipboard.writeText(window.location.origin + item.folderUrl).then(() => {
779
+ alert('Folder link copied to clipboard!');
780
  });
781
  };
782
  actionsContainer.appendChild(copyBtn);
783
 
784
  const openBtn = document.createElement('button');
785
+ openBtn.textContent = 'Open Folder';
786
  openBtn.className = 'small-btn';
787
  openBtn.onclick = () => {
788
+ window.open(window.location.origin + item.folderUrl, '_blank');
789
  };
790
  actionsContainer.appendChild(openBtn);
791
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
792
  historyItem.appendChild(actionsContainer);
793
  historyList.appendChild(historyItem);
794
  });
795
  historyModal.style.display = "block";
796
  }
797
 
798
+ function quickOpen(url, fileName) {
799
  quickOpenContent.innerHTML = '';
800
  const fullUrl = window.location.origin + url;
801
 
 
846
  return HTML_CONTENT
847
 
848
  @app.post("/upload")
849
+ async def handle_upload(files: List[UploadFile] = File(...)):
850
+ if not files:
851
+ return JSONResponse(content={"error": "No files selected."}, status_code=400)
852
 
853
  cookies = await get_cookies()
854
  if 'csrftoken' not in cookies or 'sessionid' not in cookies:
855
  return JSONResponse(content={"error": "Failed to obtain necessary cookies"}, status_code=500)
856
 
857
+ folder_id = str(uuid.uuid4())
858
+ folder_path = f"/rbxg/{folder_id}"
859
+ os.makedirs(folder_path, exist_ok=True)
860
+
861
+ uploaded_files = []
862
+ for file in files:
863
+ upload_result = await initiate_upload(cookies, file.filename, file.content_type)
864
+ if not upload_result or 'upload_url' not in upload_result:
865
+ return JSONResponse(content={"error": f"Failed to initiate upload for {file.filename}"}, status_code=500)
866
 
867
+ file_content = await file.read()
868
+ upload_success = await retry_upload(upload_result['upload_url'], file_content, file.content_type)
869
+ if not upload_success:
870
+ return JSONResponse(content={"error": f"File upload failed for {file.filename} after multiple attempts"}, status_code=500)
871
 
872
+ original_url = upload_result['serving_url']
873
+ mirrored_url = f"{folder_path}/{file.filename}"
874
+ uploaded_files.append({"name": file.filename, "url": mirrored_url})
875
 
876
+ return JSONResponse(content={"folder_url": folder_path, "files": uploaded_files})
877
 
878
+ @app.get("/rbxg/{folder_id}/{file_name}")
879
+ async def handle_file_stream(folder_id: str, file_name: str, request: Request):
880
+ original_url = f'https://replicate.delivery/pbxt/{folder_id}/{file_name}'
881
  range_header = request.headers.get('Range')
882
 
883
  headers = {'Range': range_header} if range_header else {}
 
990
  await asyncio.sleep(delay)
991
  delay = min(delay * 2, 60) # Exponential backoff, capped at 60 seconds
992
 
993
+ return False