fileuploader / app.py
coollsd's picture
Update app.py
910736e verified
raw
history blame
47.3 kB
from fastapi import FastAPI, File, UploadFile, Request
from fastapi.responses import HTMLResponse, JSONResponse, StreamingResponse
import requests
import time
import asyncio
from typing import Dict
import os
import shutil
app = FastAPI()
HTML_CONTENT = """
<!DOCTYPE html>
<html lang="en">
<head>
<!-- (HTML content remains the same as before) -->
<!-- ... [omitted for brevity, use the same HTML_CONTENT as before] ... -->
<style>
/* (Styles remain the same as before) /
</style>
</head>
<body>
<!-- (Body content remains the same as before) -->
<!-- ... [omitted for brevity, use the same HTML_CONTENT as before] ... -->
<script>
const fileInput = document.getElementById('file');
const fileName = document.getElementById('fileName');
const uploadForm = document.getElementById('uploadForm');
const progressContainer = document.getElementById('progressContainer');
const loadingSpinner = document.getElementById('loadingSpinner');
const resultContainer = document.getElementById('resultContainer');
const dropZone = document.getElementById('dropZone');
const modal = document.getElementById('embedModal');
const span = document.getElementsByClassName("close")[0];
const embedLinkInput = document.getElementById('embedLink');
const uploadBtn = document.getElementById('uploadBtn');
fileInput.addEventListener('change', handleFileSelect);
uploadForm.addEventListener('submit', (e) => {
e.preventDefault();
if (fileInput.files.length > 0) {
uploadFile(fileInput.files[0]);
}
});
dropZone.addEventListener('dragover', (e) => {
e.preventDefault();
dropZone.classList.add('drag-over');
});
dropZone.addEventListener('dragleave', () => {
dropZone.classList.remove('drag-over');
});
dropZone.addEventListener('drop', (e) => {
e.preventDefault();
dropZone.classList.remove('drag-over');
handleFileSelect({ target: { files: e.dataTransfer.files } });
});
document.addEventListener('paste', (e) => {
const items = e.clipboardData.items;
for (let i = 0; i < items.length; i++) {
if (items[i].kind === 'file') {
const file = items[i].getAsFile();
handleFileSelect({ target: { files: [file] } });
break;
}
}
});
span.onclick = function() {
modal.style.display = "none";
}
window.onclick = function(event) {
if (event.target == modal) {
modal.style.display = "none";
}
}
function handleFileSelect(e) {
if (e.target.files && e.target.files.length > 0) {
const file = e.target.files[0];
fileName.textContent = file.name;
uploadBtn.style.display = 'inline-block';
const dataTransfer = new DataTransfer();
dataTransfer.items.add(file);
fileInput.files = dataTransfer.files;
}
}
async function uploadFile(file) {
const chunkSize = 1024 * 1024; // 1 MB
const totalChunks = Math.ceil(file.size / chunkSize);
const uploadId = generateUploadId();
progressContainer.innerHTML = '';
progressContainer.style.display = 'block';
loadingSpinner.style.display = 'block';
uploadBtn.disabled = true;
resultContainer.innerHTML = '';
resultContainer.style.display = 'none';
const progressBar = createProgressBar(file.name);
progressContainer.appendChild(progressBar);
let chunkIndex = 0;
while (chunkIndex < totalChunks) {
const start = chunkIndex * chunkSize;
const end = Math.min(file.size, start + chunkSize);
const chunk = file.slice(start, end);
const formData = new FormData();
formData.append('file', chunk);
formData.append('chunkIndex', chunkIndex);
formData.append('totalChunks', totalChunks);
formData.append('uploadId', uploadId);
formData.append('fileName', file.name);
formData.append('contentType', file.type);
let success = false;
while (!success) {
try {
await uploadChunk(formData, progressBar.querySelector('.progress'), file.size, start, end);
success = true;
} catch (error) {
console.error('Chunk upload error:', error);
// Wait for a short time before retrying
await new Promise(resolve => setTimeout(resolve, 1000));
}
}
chunkIndex++;
}
// After all chunks are uploaded, notify the server to assemble them
const response = await fetch('/assemble', {
method: 'POST',
body: JSON.stringify({ uploadId: uploadId, fileName: file.name, contentType: file.type }),
headers: { 'Content-Type': 'application/json' }
});
const result = await response.json();
if (response.ok && result.url) {
addResultLink(result.url, file.name);
resetUploadState();
} else {
alert('Failed to assemble file on server.');
}
}
function generateUploadId() {
return 'xxxxxxx'.replace(/[x]/g, function() {
return Math.floor(Math.random() * 16).toString(16);
});
}
function createProgressBar(fileName) {
const progressBarContainer = document.createElement('div');
progressBarContainer.className = 'progress-bar';
const progress = document.createElement('div');
progress.className = 'progress';
progressBarContainer.appendChild(progress);
const label = document.createElement('div');
label.textContent = fileName;
label.style.fontSize = '0.8rem';
label.style.marginBottom = '5px';
const container = document.createElement('div');
container.appendChild(label);
container.appendChild(progressBarContainer);
return container;
}
function resetUploadState() {
fileInput.value = '';
fileName.textContent = '';
uploadBtn.style.display = 'none';
uploadBtn.disabled = false;
loadingSpinner.style.display = 'none';
}
function updateProgress(event, progressBar) {
if (event.lengthComputable) {
const percentComplete = (event.loaded / event.total) * 100;
progressBar.style.width = percentComplete + '%';
}
}
function addResultLink(url, fileName) {
const linkContainer = document.createElement('div');
linkContainer.style.marginBottom = '10px';
const link = document.createElement('a');
link.href = url;
link.textContent = View ${fileName};
link.className = 'result-link';
link.target = 'blank';
linkContainer.appendChild(link);
const buttonsContainer = document.createElement('div');
buttonsContainer.className = 'link-buttons';
const copyBtn = document.createElement('button');
copyBtn.textContent = 'Copy Link';
copyBtn.className = 'small-btn copy-btn';
copyBtn.onclick = () => {
navigator.clipboard.writeText(window.location.origin + url).then(() => {
alert('Link copied to clipboard!');
});
};
buttonsContainer.appendChild(copyBtn);
if (fileName.toLowerCase().endsWith('.mp4')) {
const embedBtn = document.createElement('button');
embedBtn.textContent = 'Embed Video for Discord';
embedBtn.className = 'small-btn embed-btn';
embedBtn.onclick = () => {
showEmbedModal(url);
};
buttonsContainer.appendChild(embedBtn);
}
linkContainer.appendChild(buttonsContainer);
resultContainer.appendChild(linkContainer);
resultContainer.style.display = 'block';
}
function showEmbedModal(url) {
const embedUrl = ${window.location.origin}/embed?url=${encodeURIComponent(window.location.origin + url)}&thumbnail=${encodeURIComponent('https://coollsd-fileuploader.hf.space/rbxg/LdnShkhZFgMlMmCwkeX78RqHbiol6r554v5BryQS9upEC1wu/Untitled.png')};
embedLinkInput.value = embedUrl;
modal.style.display = "block";
}
function copyEmbedLink() {
embedLinkInput.select();
document.execCommand('copy');
alert('Embed link copied to clipboard!');
}
async function uploadChunk(formData, progressBar, totalSize, start, end) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open('POST', '/upload_chunk', true);
xhr.upload.onprogress = (event) => {
if (event.lengthComputable) {
const chunkProgress = (event.loaded / (end - start)) * 100;
const totalProgress = ((start + event.loaded) / totalSize) * 100;
progressBar.style.width = totalProgress + '%';
}
};
xhr.onload = function() {
if (xhr.status === 200) {
resolve();
} else {
reject(new Error(Chunk upload failed with status ${xhr.status}));
}
};
xhr.onerror = function() {
reject(new Error('Network error occurred during chunk upload'));
};
xhr.send(formData);
});
}
</script>
</body>
</html>
"""
@app.get("/", response_class=HTMLResponse)
async def index():
return HTML_CONTENT
@app.post("/upload_chunk")
async def handle_upload_chunk(request: Request):
form = await request.form()
chunk = form['file'].file
chunk_index = int(form['chunkIndex'])
total_chunks = int(form['totalChunks'])
upload_id = form['uploadId']
file_name = form['fileName']
content_type = form['contentType']
temp_dir = f"temp_uploads/{upload_id}"
os.makedirs(temp_dir, exist_ok=True)
chunk_path = os.path.join(temp_dir, f"chunk{chunk_index}")
with open(chunk_path, 'wb') as f:
while True:
data = chunk.read(1024 * 1024)
if not data:
break
f.write(data)
return JSONResponse(content={"status": "chunk received"})
@app.post("/assemble")
async def assemble_chunks(request: Request):
data = await request.json()
upload_id = data['uploadId']
file_name = data['fileName']
content_type = data['contentType']
temp_dir = f"temp_uploads/{upload_id}"
chunk_files = sorted(os.listdir(temp_dir), key=lambda x: int(x.split('_')[1]))
file_path = os.path.join(temp_dir, file_name)
with open(file_path, 'wb') as outfile:
for chunk_file in chunk_files:
chunk_path = os.path.join(temp_dir, chunk_file)
with open(chunk_path, 'rb') as infile:
outfile.write(infile.read())
# Now proceed to upload the file to replicate.com as before
cookies = await get_cookies()
upload_result = await initiate_upload(cookies, file_name, content_type)
if not upload_result or 'upload_url' not in upload_result:
return JSONResponse(content={"error": "Failed to initiate upload"}, status_code=500)
# Read the assembled file
with open(file_path, 'rb') as f:
file_content = f.read()
upload_success = await retry_upload(upload_result['upload_url'], file_content, content_type)
if not upload_success:
return JSONResponse(content={"error": "File upload failed after multiple attempts"}, status_code=500)
original_url = upload_result['serving_url']
mirrored_url = f"/rbxg/{original_url.split('/pbxt/')[1]}"
# Clean up temp files
shutil.rmtree(temp_dir)
return JSONResponse(content={"url": mirrored_url})
@app.get("/rbxg/{path:path}")
async def handle_video_stream(path: str, request: Request):
original_url = f'https://replicate.delivery/pbxt/{path}'
range_header = request.headers.get('Range')
headers = {'Range': range_header} if range_header else {}
response = requests.get(original_url, headers=headers, stream=True)
def generate():
for chunk in response.iter_content(chunk_size=8192):
yield chunk
headers = dict(response.headers)
headers['Access-Control-Allow-Origin'] = ''
headers['Content-Disposition'] = 'inline'
if response.status_code == 206:
headers['Content-Range'] = response.headers.get('Content-Range')
return StreamingResponse(generate(), status_code=response.status_code, headers=headers)
@app.get("/embed")
async def embed_video(url: str, thumbnail: str):
html = f'''
<html>
<head>
<meta property="og:type" content="video.other">
<meta property="og:video" content="{url}">
<meta property="og:video:url" content="{url}">
<meta property="og:video:secure_url" content="{url}">
<meta property="og:video:type" content="video/mp4">
<meta property="og:video:width" content="1280">
<meta property="og:video:height" content="720">
<meta property="og:image" content="{thumbnail}">
<meta property="og:image:secure_url" content="{thumbnail}">
<meta property="og:image:width" content="1280">
<meta property="og:image:height" content="720">
<meta property="og:image:type" content="image/png">
<style>
body, html {{ margin: 0; padding: 0; height: 100%; background: #000; }}
#thumbnail {{ width: 100%; height: 100%; object-fit: contain; cursor: pointer; }}
#video {{ display: none; width: 100%; height: 100%; object-fit: contain; }}
</style>
</head>
<body>
<img id="thumbnail" src="{thumbnail}" onclick="playVideo()">
<video id="video" controls autoplay>
<source src="{url}" type="video/mp4">
Your browser does not support the video tag.
</video>
<script>
function playVideo() {{
document.getElementById('thumbnail').style.display = 'none';
document.getElementById('video').style.display = 'block';
document.getElementById('video').play();
}}
</script>
</body>
</html>
'''
return HTMLResponse(content=html)
async def get_cookies() -> Dict[str, str]:
try:
response = requests.get('https://replicate.com/levelsio/neon-tokyo', headers={
'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'
})
return dict(response.cookies)
except Exception as e:
print(f'Error fetching the page: {e}')
return {}
async def initiate_upload(cookies: Dict[str, str], filename: str, content_type: str) -> Dict:
url = f'https://replicate.com/api/upload/{filename}?content_type={content_type}'
try:
response = requests.post(url, cookies=cookies, headers={
'X-CSRFToken': cookies.get('csrftoken'),
'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',
'Referer': 'https://replicate.com/levelsio/neon-tokyo',
'Origin': 'https://replicate.com',
'Accept': '/',
'Accept-Language': 'en-US,en;q=0.5',
'Accept-Encoding': 'identity',
'Sec-Fetch-Dest': 'empty',
'Sec-Fetch-Mode': 'cors',
'Sec-Fetch-Site': 'same-origin',
'Sec-GPC': '1',
'Priority': 'u=1, i'
})
return response.json()
except Exception as e:
print(f'Error initiating upload: {e}')
raise
async def upload_file(upload_url: str, file_content: bytes, content_type: str) -> bool:
try:
response = requests.put(upload_url, data=file_content, headers={'Content-Type': content_type})
return response.status_code == 200
except Exception as e:
print(f'Error uploading file: {e}')
return False
async def retry_upload(upload_url: str, file_content: bytes, content_type: str, max_retries: int = 5, delay: int = 1) -> bool:
retries = 0
while retries < max_retries:
try:
success = await upload_file(upload_url, file_content, content_type)
if success:
return True
print("Upload failed. Retrying...")
except Exception as e:
print(f"Error during upload: {e}")
await asyncio.sleep(delay)
delay = min(delay * 2, 60) # Exponential backoff, capped at 60 seconds
retries += 1
return False
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.
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.
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.
send the entire full script without placehodlers
Certainly! Here's the entire full script without any placeholders:
from fastapi import FastAPI, File, UploadFile, Request
from fastapi.responses import HTMLResponse, JSONResponse, StreamingResponse
import requests
import time
import asyncio
from typing import Dict
import os
import shutil
app = FastAPI()
HTML_CONTENT = """
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Radd PRO Uploader</title>
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;600&display=swap" rel="stylesheet">
<style>
body {
font-family: 'Poppins', sans-serif;
background-color: #121212;
color: #e0e0e0;
margin: 0;
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
padding: 20px;
box-sizing: border-box;
}
body::before {
content: "";
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAAA8CAQAAACM/9unAAAACXBIWXMAAAsTAAALEwEAmpwYAAABF0lEQVR4nO3YMRLCQBRE0clsYgxZwwq7oLGxAgp2AtbsqQZsYgEG0wEWc/LSZnb9HcMjR8fHvEKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgsK8tJOq9fzs7vlkNlWvf5ELi+20t5k2VSL7p/iHJ5Rz5ynOhU7rQ4pUi+6b4hyeUc+cpzoVO60OkV7rQ4pUi+6b4hxenu+en4rQAOkaks/eJegGaUfOHI+nRvQCND1w2J9yf8C2KZwNjkeUsAKRa7Nz50tQAokVbO/U6AaiWjl5wd6kQAOke8Xpx3JgTQKVq7Heh0ADpFvF6cd0kE0Claux3odAA6RfxenHc+BNCpWrsd6HQAOkX8Xpx3PgTQqVq7Heh0ADpF/F6cdz4E0KlarjdI/cR1FBQUGBgYGBgYGBgYG/AT4AIHph6aQGU7oAAAAASUVORK5CYII=') repeat;
animation: grain 8s steps(10) infinite;
opacity: 0.2;
pointer-events: none;
}
@keyframes grain {
0% { transform: translate(0, 0); }
10% { transform: translate(-5%, -5%); }
20% { transform: translate(-10%, 5%); }
30% { transform: translate(5%, -10%); }
40% { transform: translate(-5%, 15%); }
50% { transform: translate(-10%, 5%); }
60% { transform: translate(15%, 0); }
70% { transform: translate(0, 10%); }
80% { transform: translate(-15%, 0); }
90% { transform: translate(10%, 5%); }
100% { transform: translate(5%, 0); }
}
.container {
position: relative;
width: 100%;
max-width: 450px;
margin: 0 auto;
padding: 2rem;
background: rgba(18, 18, 18, 0.9);
backdrop-filter: blur(10px);
border-radius: 15px;
z-index: 1;
text-align: center;
box-shadow: 0 0 20px rgba(0, 0, 0, 0.8);
}
h1 {
margin-bottom: 1.5rem;
font-size: 1.8rem;
color: #ffffff;
text-shadow: 0 0 5px rgba(255, 255, 255, 0.2);
}
.btn {
display: inline-block;
position: relative;
padding: 12px 24px;
margin: 0.5rem;
font-size: 1rem;
font-weight: 600;
color: #ffffff;
background-color: #2a2a2a;
border: none;
border-radius: 5px;
cursor: pointer;
overflow: hidden;
z-index: 1;
transition: color 0.3s ease, box-shadow 0.3s ease;
}
.btn:hover {
color: #ffffff;
box-shadow: 0 0 15px rgba(200, 200, 200, 0.5);
}
.btn:hover::before {
content: '';
position: absolute;
inset: -10px;
background: radial-gradient(circle at center, rgba(200,200,200,0.2), transparent);
filter: blur(20px);
animation: glowAnimation 2s infinite;
z-index: -1;
pointer-events: none;
}
@keyframes glowAnimation {
0% { transform: scale(0.8); }
50% { transform: scale(1.2); }
100% { transform: scale(0.8); }
}
.btn:active {
transform: scale(0.98);
}
.small-btn {
padding: 6px 12px;
font-size: 0.8rem;
font-weight: 500;
background-color: #2a2a2a;
color: #ffffff;
border: none;
border-radius: 5px;
cursor: pointer;
transition: color 0.3s ease, box-shadow 0.3s ease;
position: relative;
overflow: hidden;
z-index: 1;
margin: 0.25rem;
}
.small-btn:hover {
color: #ffffff;
box-shadow: 0 0 10px rgba(200, 200, 200, 0.5);
}
.small-btn:hover::before {
content: '';
position: absolute;
inset: -10px;
background: radial-gradient(circle at center, rgba(200,200,200,0.2), transparent);
filter: blur(15px);
animation: glowAnimation 2s infinite;
z-index: -1;
pointer-events: none;
}
.small-btn:active {
transform: scale(0.98);
}
.drop-zone {
position: relative;
padding: 20px;
margin-bottom: 1rem;
border: 2px dashed #aaa;
border-radius: 10px;
cursor: pointer;
transition: all 0.3s ease;
background: rgba(255, 255, 255, 0.05);
overflow: hidden;
}
.drop-zone:hover, .drop-zone.drag-over {
border-color: #ffffff;
background: rgba(255, 255, 255, 0.1);
position: relative;
}
.drop-zone:hover::before, .drop-zone.drag-over::before {
content: '';
position: absolute;
inset: -10px;
background: radial-gradient(circle at center, rgba(200,200,200,0.2), transparent);
filter: blur(30px);
animation: grainGlow 5s infinite;
z-index: -1;
pointer-events: none;
}
@keyframes grainGlow {
0% { opacity: 0.2; }
50% { opacity: 0.5; }
100% { opacity: 0.2; }
}
.file-input {
display: none;
}
.file-name {
margin-top: 1rem;
font-size: 0.9rem;
color: #aaa;
word-break: break-all;
}
.progress-container {
display: none;
margin-top: 1.5rem;
}
.progress-bar {
width: 100%;
height: 10px;
background-color: #333;
border-radius: 5px;
overflow: hidden;
margin-bottom: 10px;
}
.progress {
width: 0%;
height: 100%;
background-color: #ffffff;
transition: width 0.3s ease;
}
.loading-spinner {
display: none;
width: 40px;
height: 40px;
border: 4px solid #333;
border-top: 4px solid #ffffff;
border-radius: 50%;
animation: spin 1s linear infinite;
margin: 20px auto;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.result-container {
display: none;
margin-top: 1.5rem;
}
.result-link {
color: #ffffff;
text-decoration: none;
font-weight: 600;
transition: all 0.3s ease;
margin-right: 10px;
word-break: break-all;
}
.result-link:hover {
text-decoration: underline;
}
.link-buttons {
display: flex;
justify-content: center;
flex-wrap: wrap;
margin-top: 10px;
}
/* File Types /
.file-types {
margin-top: 2rem;
font-size: 0.8rem;
color: #aaa;
}
.modal {
display: none;
position: fixed;
z-index: 2;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: auto;
background-color: rgba(0,0,0,0.8);
}
.modal-content {
background-color: #1e1e1e;
margin: 15% auto;
padding: 20px;
border: 1px solid #333;
width: 90%;
max-width: 600px;
border-radius: 10px;
color: #e0e0e0;
animation: modalFadeIn 0.3s;
position: relative;
}
@keyframes modalFadeIn {
from {opacity: 0; transform: scale(0.8);}
to {opacity: 1; transform: scale(1);}
}
.close {
color: #aaa;
position: absolute;
top: 10px;
right: 15px;
font-size: 28px;
font-weight: bold;
transition: color 0.3s ease;
}
.close:hover,
.close:focus {
color: #fff;
text-decoration: none;
cursor: pointer;
}
.embed-container {
display: flex;
flex-direction: column;
align-items: stretch;
margin-top: 15px;
}
#embedLink {
width: 100%;
padding: 10px;
background-color: #333;
border: 1px solid #555;
color: #e0e0e0;
border-radius: 5px;
margin-bottom: 10px;
font-size: 0.9rem;
}
@media (max-width: 480px) {
.container {
padding: 1.5rem;
}
h1 {
font-size: 1.5rem;
}
.btn, .small-btn {
font-size: 0.9rem;
padding: 10px 20px;
}
.file-types {
font-size: 0.7rem;
}
.modal-content {
width: 95%;
margin: 10% auto;
}
}
</style>
</head>
<body>
<div class="container">
<h1>Radd PRO Uploader</h1>
<form id="uploadForm">
<div id="dropZone" class="drop-zone">
<input type="file" name="file" id="file" class="file-input" accept=".zip,.mp4,.txt,.mp3,image/,.pdf" required>
<label for="file" class="btn">Choose File</label>
<p>or drag and drop file here/paste image</p>
</div>
<div class="file-name" id="fileName"></div>
<button type="submit" id="uploadBtn" class="btn" style="display: none; margin-top: 1rem;">Upload File</button>
<div class="progress-container" id="progressContainer"></div>
<div class="loading-spinner" id="loadingSpinner"></div>
</form>
<div class="result-container" id="resultContainer"></div>
<div class="file-types">
Allowed file types: .zip, .mp4, .txt, .mp3, all image types, .pdf
</div>
</div>
<div id="embedModal" class="modal">
<div class="modal-content">
<span class="close">×</span>
<h2>Embed Video Link</h2>
<p>copy the link to embed it on discord:</p>
<div class="embed-container">
<input type="text" id="embedLink" readonly>
<button onclick="copyEmbedLink()" class="small-btn copy-embed-btn">Copy</button>
</div>
</div>
</div>
<script>
const fileInput = document.getElementById('file');
const fileName = document.getElementById('fileName');
const uploadForm = document.getElementById('uploadForm');
const progressContainer = document.getElementById('progressContainer');
const loadingSpinner = document.getElementById('loadingSpinner');
const resultContainer = document.getElementById('resultContainer');
const dropZone = document.getElementById('dropZone');
const modal = document.getElementById('embedModal');
const span = document.getElementsByClassName("close")[0];
const embedLinkInput = document.getElementById('embedLink');
const uploadBtn = document.getElementById('uploadBtn');
fileInput.addEventListener('change', handleFileSelect);
uploadForm.addEventListener('submit', (e) => {
e.preventDefault();
if (fileInput.files.length > 0) {
uploadFile(fileInput.files[0]);
}
});
dropZone.addEventListener('dragover', (e) => {
e.preventDefault();
dropZone.classList.add('drag-over');
});
dropZone.addEventListener('dragleave', () => {
dropZone.classList.remove('drag-over');
});
dropZone.addEventListener('drop', (e) => {
e.preventDefault();
dropZone.classList.remove('drag-over');
handleFileSelect({ target: { files: e.dataTransfer.files } });
});
document.addEventListener('paste', (e) => {
const items = e.clipboardData.items;
for (let i = 0; i < items.length; i++) {
if (items[i].kind === 'file') {
const file = items[i].getAsFile();
handleFileSelect({ target: { files: [file] } });
break;
}
}
});
span.onclick = function() {
modal.style.display = "none";
}
window.onclick = function(event) {
if (event.target == modal) {
modal.style.display = "none";
}
}
function handleFileSelect(e) {
if (e.target.files && e.target.files.length > 0) {
const file = e.target.files[0];
fileName.textContent = file.name;
uploadBtn.style.display = 'inline-block';
const dataTransfer = new DataTransfer();
dataTransfer.items.add(file);
fileInput.files = dataTransfer.files;
}
}
async function uploadFile(file) {
const chunkSize = 1024 * 1024; // 1 MB
const totalChunks = Math.ceil(file.size / chunkSize);
const uploadId = generateUploadId();
progressContainer.innerHTML = '';
progressContainer.style.display = 'block';
loadingSpinner.style.display = 'block';
uploadBtn.disabled = true;
resultContainer.innerHTML = '';
resultContainer.style.display = 'none';
const progressBar = createProgressBar(file.name);
progressContainer.appendChild(progressBar);
let chunkIndex = 0;
while (chunkIndex < totalChunks) {
const start = chunkIndex * chunkSize;
const end = Math.min(file.size, start + chunkSize);
const chunk = file.slice(start, end);
const formData = new FormData();
formData.append('file', chunk);
formData.append('chunkIndex', chunkIndex);
formData.append('totalChunks', totalChunks);
formData.append('uploadId', uploadId);
formData.append('fileName', file.name);
formData.append('contentType', file.type);
let success = false;
while (!success) {
try {
await uploadChunk(formData, progressBar.querySelector('.progress'), file.size, start, end);
success = true;
} catch (error) {
console.error('Chunk upload error:', error);
// Wait for a short time before retrying
await new Promise(resolve => setTimeout(resolve, 1000));
}
}
chunkIndex++;
}
// After all chunks are uploaded, notify the server to assemble them
const response = await fetch('/assemble', {
method: 'POST',
body: JSON.stringify({ uploadId: uploadId, fileName: file.name, contentType: file.type }),
headers: { 'Content-Type': 'application/json' }
});
const result = await response.json();
if (response.ok && result.url) {
addResultLink(result.url, file.name);
resetUploadState();
} else {
alert('Failed to assemble file on server.');
}
}
function generateUploadId() {
return 'xxxxxxx'.replace(/[x]/g, function() {
return Math.floor(Math.random() * 16).toString(16);
});
}
function createProgressBar(fileName) {
const progressBarContainer = document.createElement('div');
progressBarContainer.className = 'progress-bar';
const progress = document.createElement('div');
progress.className = 'progress';
progressBarContainer.appendChild(progress);
const label = document.createElement('div');
label.textContent = fileName;
label.style.fontSize = '0.8rem';
label.style.marginBottom = '5px';
const container = document.createElement('div');
container.appendChild(label);
container.appendChild(progressBarContainer);
return container;
}
function resetUploadState() {
fileInput.value = '';
fileName.textContent = '';
uploadBtn.style.display = 'none';
uploadBtn.disabled = false;
loadingSpinner.style.display = 'none';
}
function updateProgress(event, progressBar) {
if (event.lengthComputable) {
const percentComplete = (event.loaded / event.total) * 100;
progressBar.style.width = percentComplete + '%';
}
}
function addResultLink(url, fileName) {
const linkContainer = document.createElement('div');
linkContainer.style.marginBottom = '10px';
const link = document.createElement('a');
link.href = url;
link.textContent = View ${fileName};
link.className = 'result-link';
link.target = 'blank';
linkContainer.appendChild(link);
const buttonsContainer = document.createElement('div');
buttonsContainer.className = 'link-buttons';
const copyBtn = document.createElement('button');
copyBtn.textContent = 'Copy Link';
copyBtn.className = 'small-btn copy-btn';
copyBtn.onclick = () => {
navigator.clipboard.writeText(window.location.origin + url).then(() => {
alert('Link copied to clipboard!');
});
};
buttonsContainer.appendChild(copyBtn);
if (fileName.toLowerCase().endsWith('.mp4')) {
const embedBtn = document.createElement('button');
embedBtn.textContent = 'Embed Video for Discord';
embedBtn.className = 'small-btn embed-btn';
embedBtn.onclick = () => {
showEmbedModal(url);
};
buttonsContainer.appendChild(embedBtn);
}
linkContainer.appendChild(buttonsContainer);
resultContainer.appendChild(linkContainer);
resultContainer.style.display = 'block';
}
function showEmbedModal(url) {
const embedUrl = ${window.location.origin}/embed?url=${encodeURIComponent(window.location.origin + url)}&thumbnail=${encodeURIComponent('https://coollsd-fileuploader.hf.space/rbxg/LdnShkhZFgMlMmCwkeX78RqHbiol6r554v5BryQS9upEC1wu/Untitled.png')};
embedLinkInput.value = embedUrl;
modal.style.display = "block";
}
function copyEmbedLink() {
embedLinkInput.select();
document.execCommand('copy');
alert('Embed link copied to clipboard!');
}
async function uploadChunk(formData, progressBar, totalSize, start, end) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open('POST', '/upload_chunk', true);
xhr.upload.onprogress = (event) => {
if (event.lengthComputable) {
const chunkProgress = (event.loaded / (end - start)) * 100;
const totalProgress = ((start + event.loaded) / totalSize) * 100;
progressBar.style.width = totalProgress + '%';
}
};
xhr.onload = function() {
if (xhr.status === 200) {
resolve();
} else {
reject(new Error(Chunk upload failed with status ${xhr.status}));
}
};
xhr.onerror = function() {
reject(new Error('Network error occurred during chunk upload'));
};
xhr.send(formData);
});
}
</script>
</body>
</html>
"""
@app.get("/", response_class=HTMLResponse)
async def index():
return HTML_CONTENT
@app.post("/upload_chunk")
async def handle_upload_chunk(request: Request):
form = await request.form()
chunk = form['file'].file
chunk_index = int(form['chunkIndex'])
total_chunks = int(form['totalChunks'])
upload_id = form['uploadId']
file_name = form['fileName']
content_type = form['contentType']
temp_dir = f"temp_uploads/{upload_id}"
os.makedirs(temp_dir, exist_ok=True)
chunk_path = os.path.join(temp_dir, f"chunk{chunk_index}")
with open(chunk_path, 'wb') as f:
while True:
data = chunk.read(1024 * 1024)
if not data:
break
f.write(data)
return JSONResponse(content={"status": "chunk received"})
@app.post("/assemble")
async def assemble_chunks(request: Request):
data = await request.json()
upload_id = data['uploadId']
file_name = data['fileName']
content_type = data['contentType']
temp_dir = f"temp_uploads/{upload_id}"
chunk_files = sorted(os.listdir(temp_dir), key=lambda x: int(x.split('_')[1]))
file_path = os.path.join(temp_dir, file_name)
with open(file_path, 'wb') as outfile:
for chunk_file in chunk_files:
chunk_path = os.path.join(temp_dir, chunk_file)
with open(chunk_path, 'rb') as infile:
outfile.write(infile.read())
# Now proceed to upload the file to replicate.com as before
cookies = await get_cookies()
upload_result = await initiate_upload(cookies, file_name, content_type)
if not upload_result or 'upload_url' not in upload_result:
return JSONResponse(content={"error": "Failed to initiate upload"}, status_code=500)
# Read the assembled file
with open(file_path, 'rb') as f:
file_content = f.read()
upload_success = await retry_upload(upload_result['upload_url'], file_content, content_type)
if not upload_success:
return JSONResponse(content={"error": "File upload failed after multiple attempts"}, status_code=500)
original_url = upload_result['serving_url']
mirrored_url = f"/rbxg/{original_url.split('/pbxt/')[1]}"
# Clean up temp files
shutil.rmtree(temp_dir)
return JSONResponse(content={"url": mirrored_url})
@app.get("/rbxg/{path:path}")
async def handle_video_stream(path: str, request: Request):
original_url = f'https://replicate.delivery/pbxt/{path}'
range_header = request.headers.get('Range')
headers = {'Range': range_header} if range_header else {}
response = requests.get(original_url, headers=headers, stream=True)
def generate():
for chunk in response.iter_content(chunk_size=8192):
yield chunk
headers = dict(response.headers)
headers['Access-Control-Allow-Origin'] = ''
headers['Content-Disposition'] = 'inline'
if response.status_code == 206:
headers['Content-Range'] = response.headers.get('Content-Range')
return StreamingResponse(generate(), status_code=response.status_code, headers=headers)
@app.get("/embed")
async def embed_video(url: str, thumbnail: str):
html = f'''
<html>
<head>
<meta property="og:type" content="video.other">
<meta property="og:video" content="{url}">
<meta property="og:video:url" content="{url}">
<meta property="og:video:secure_url" content="{url}">
<meta property="og:video:type" content="video/mp4">
<meta property="og:video:width" content="1280">
<meta property="og:video:height" content="720">
<meta property="og:image" content="{thumbnail}">
<meta property="og:image:secure_url" content="{thumbnail}">
<meta property="og:image:width" content="1280">
<meta property="og:image:height" content="720">
<meta property="og:image:type" content="image/png">
<style>
body, html {{ margin: 0; padding: 0; height: 100%; background: #000; }}
#thumbnail {{ width: 100%; height: 100%; object-fit: contain; cursor: pointer; }}
#video {{ display: none; width: 100%; height: 100%; object-fit: contain; }}
</style>
</head>
<body>
<img id="thumbnail" src="{thumbnail}" onclick="playVideo()">
<video id="video" controls autoplay>
<source src="{url}" type="video/mp4">
Your browser does not support the video tag.
</video>
<script>
function playVideo() {{
document.getElementById('thumbnail').style.display = 'none';
document.getElementById('video').style.display = 'block';
document.getElementById('video').play();
}}
</script>
</body>
</html>
'''
return HTMLResponse(content=html)
async def get_cookies() -> Dict[str, str]:
try:
response = requests.get('https://replicate.com/levelsio/neon-tokyo', headers={
'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'
})
return dict(response.cookies)
except Exception as e:
print(f'Error fetching the page: {e}')
return {}
async def initiate_upload(cookies: Dict[str, str], filename: str, content_type: str) -> Dict:
url = f'https://replicate.com/api/upload/{filename}?content_type={content_type}'
try:
response = requests.post(url, cookies=cookies, headers={
'X-CSRFToken': cookies.get('csrftoken'),
'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',
'Referer': 'https://replicate.com/levelsio/neon-tokyo',
'Origin': 'https://replicate.com',
'Accept': '/*',
'Accept-Language': 'en-US,en;q=0.5',
'Accept-Encoding': 'identity',
'Sec-Fetch-Dest': 'empty',
'Sec-Fetch-Mode': 'cors',
'Sec-Fetch-Site': 'same-origin',
'Sec-GPC': '1',
'Priority': 'u=1, i'
})
return response.json()
except Exception as e:
print(f'Error initiating upload: {e}')
raise
async def upload_file(upload_url: str, file_content: bytes, content_type: str) -> bool:
try:
response = requests.put(upload_url, data=file_content, headers={'Content-Type': content_type})
return response.status_code == 200
except Exception as e:
print(f'Error uploading file: {e}')
return False
async def retry_upload(upload_url: str, file_content: bytes, content_type: str, max_retries: int = 5, delay: int = 1) -> bool:
retries = 0
while retries < max_retries:
try:
success = await upload_file(upload_url, file_content, content_type)
if success:
return True
print("Upload failed. Retrying...")
except Exception as e:
print(f"Error during upload: {e}")
await asyncio.sleep(delay)
delay = min(delay * 2, 60) # Exponential backoff, capped at 60 seconds
retries += 1
return False