fileuploader / app.py
coollsd's picture
Update app.py
ebde77a verified
raw
history blame
17.2 kB
from fastapi import FastAPI, File, UploadFile, Request
from fastapi.responses import HTMLResponse, JSONResponse, StreamingResponse
import requests
import asyncio
from typing import Dict
app = FastAPI()
HTML_CONTENT = """
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Radd PRO Uploader</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- Google Fonts and Icons -->
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;700&display=swap" rel="stylesheet">
<!-- Material Icons -->
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<style>
/* Reset and base styles */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Roboto', sans-serif;
background: linear-gradient(135deg, #667eea, #764ba2);
color: #fff;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
}
h1 {
font-size: 2rem;
margin-bottom: 1rem;
text-align: center;
animation: fadeInDown 1s ease forwards;
}
.container {
background: rgba(255, 255, 255, 0.1);
padding: 2rem;
border-radius: 16px;
backdrop-filter: blur(10px);
max-width: 500px;
width: 90%;
text-align: center;
animation: fadeInUp 1s ease forwards;
}
.drop-zone {
border: 2px dashed rgba(255, 255, 255, 0.5);
border-radius: 12px;
padding: 2rem;
cursor: pointer;
transition: background 0.3s, border-color 0.3s;
position: relative;
overflow: hidden;
}
.drop-zone.hover {
background: rgba(255, 255, 255, 0.1);
border-color: #fff;
}
.drop-zone::before {
content: "";
position: absolute;
top: -50%;
left: -50%;
width: 200%;
height: 200%;
background: radial-gradient(circle at center, rgba(255, 255, 255, 0.2), transparent 70%);
opacity: 0;
pointer-events: none;
transition: opacity 0.5s;
}
.drop-zone.hover::before {
opacity: 1;
animation: pulsate 1.5s infinite;
}
.drop-zone i {
font-size: 4rem;
color: rgba(255, 255, 255, 0.7);
}
.drop-zone p {
margin-top: 1rem;
font-size: 1rem;
color: rgba(255, 255, 255, 0.8);
}
.file-input {
display: none;
}
.btn {
margin-top: 1.5rem;
padding: 1rem 2rem;
background: #6a11cb;
background: linear-gradient(to right, #2575fc, #6a11cb);
border: none;
border-radius: 50px;
color: #fff;
font-size: 1rem;
font-weight: 500;
cursor: pointer;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
transition: transform 0.2s ease, box-shadow 0.2s ease;
position: relative;
overflow: hidden;
}
.btn::before {
content: "";
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(to right, rgba(255,255,255,0.2), rgba(255,255,255,0.1), rgba(255,255,255,0));
transform: skewX(-45deg);
transition: left 0.5s;
}
.btn:hover::before {
left: 100%;
}
.btn:hover {
transform: scale(1.02);
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.3);
}
.progress-container {
margin-top: 1.5rem;
display: none;
}
.progress-bar {
width: 100%;
height: 10px;
background: rgba(255, 255, 255, 0.2);
border-radius: 50px;
overflow: hidden;
}
.progress {
width: 0%;
height: 100%;
background: linear-gradient(to right, #36d1dc, #5b86e5);
border-radius: 50px;
transition: width 0.3s ease;
}
.result-container {
margin-top: 1.5rem;
display: none;
animation: fadeIn 1s ease forwards;
}
.result-link {
color: #fff;
text-decoration: none;
word-wrap: break-word;
font-weight: 500;
transition: color 0.3s;
}
.result-link:hover {
color: #c7c7c7;
}
.loading-spinner {
margin-top: 1.5rem;
display: none;
}
.loading-spinner::after {
content: '';
width: 40px;
height: 40px;
border: 6px solid rgba(255, 255, 255, 0.2);
border-top-color: #fff;
border-radius: 50%;
animation: spin 1s linear infinite;
display: inline-block;
}
.file-types {
margin-top: 2rem;
font-size: 0.9rem;
color: rgba(255, 255, 255, 0.7);
animation: fadeIn 1s ease forwards;
animation-delay: 0.5s;
}
@keyframes fadeInDown {
from {
opacity: 0;
transform: translateY(-20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
@keyframes pulsate {
0% {
opacity: 0.2;
}
50% {
opacity: 0.4;
}
100% {
opacity: 0.2;
}
}
@media (max-width: 480px) {
.container {
padding: 1.5rem;
}
h1 {
font-size: 1.5rem;
}
}
</style>
</head>
<body>
<div class="container">
<h1>Radd PRO Uploader</h1>
<form id="uploadForm">
<div id="dropZone" class="drop-zone">
<i class="material-icons">cloud_upload</i>
<p>Drag & Drop or Click to Upload</p>
<input type="file" name="file" id="file" class="file-input" accept=".zip,.mp4,.txt,.mp3,image/*,.pdf" required>
</div>
<button type="submit" id="uploadBtn" class="btn" style="display: none;">Upload File</button>
<div class="progress-container" id="progressContainer">
<div class="progress-bar">
<div class="progress" id="progress"></div>
</div>
</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>
<script>
const fileInput = document.getElementById('file');
const uploadForm = document.getElementById('uploadForm');
const progressContainer = document.getElementById('progressContainer');
const progressBar = document.getElementById('progress');
const loadingSpinner = document.getElementById('loadingSpinner');
const resultContainer = document.getElementById('resultContainer');
const dropZone = document.getElementById('dropZone');
const uploadBtn = document.getElementById('uploadBtn');
dropZone.addEventListener('click', () => {
fileInput.click();
});
['dragenter', 'dragover'].forEach(eventName => {
dropZone.addEventListener(eventName, (e) => {
e.preventDefault();
e.stopPropagation();
dropZone.classList.add('hover');
});
});
['dragleave', 'drop'].forEach(eventName => {
dropZone.addEventListener(eventName, (e) => {
e.preventDefault();
e.stopPropagation();
dropZone.classList.remove('hover');
});
});
dropZone.addEventListener('drop', (e) => {
e.preventDefault();
fileInput.files = e.dataTransfer.files;
handleFileSelect();
});
fileInput.addEventListener('change', handleFileSelect);
function handleFileSelect() {
if (fileInput.files && fileInput.files.length > 0) {
uploadBtn.style.display = 'block';
const dataTransfer = new DataTransfer();
dataTransfer.items.add(fileInput.files[0]);
fileInput.files = dataTransfer.files;
} else {
uploadBtn.style.display = 'none';
}
}
uploadForm.addEventListener('submit', (e) => {
e.preventDefault();
if (fileInput.files.length > 0) {
uploadFile(fileInput.files[0]);
}
});
async function uploadFile(file) {
progressContainer.style.display = 'block';
loadingSpinner.style.display = 'inline-block';
uploadBtn.disabled = true;
resultContainer.style.display = 'none';
progressBar.style.width = '0%';
const formData = new FormData();
formData.append('file', file);
const xhr = new XMLHttpRequest();
xhr.open('POST', '/upload', true);
xhr.upload.onprogress = (event) => {
if (event.lengthComputable) {
const percentComplete = ((event.loaded / event.total) * 100).toFixed(2);
progressBar.style.width = percentComplete + '%';
}
};
xhr.onload = function () {
if (xhr.status === 200) {
const response = JSON.parse(xhr.responseText);
displayResult(response.url, file.name);
resetForm();
} else {
alert('An error occurred during the upload. Please try again.');
resetForm();
}
};
xhr.onerror = function () {
alert('An error occurred during the upload. Please try again.');
resetForm();
};
xhr.send(formData);
}
function resetForm() {
fileInput.value = '';
uploadBtn.style.display = 'none';
uploadBtn.disabled = false;
loadingSpinner.style.display = 'none';
progressContainer.style.display = 'none';
}
function displayResult(url, fileName) {
resultContainer.innerHTML = `
<p>File uploaded successfully:</p>
<a href="${url}" target="_blank" class="result-link">${fileName}</a>
`;
resultContainer.style.display = 'block';
}
</script>
</body>
</html>
"""
@app.get("/", response_class=HTMLResponse)
async def index():
return HTML_CONTENT
@app.post("/upload")
async def handle_upload(file: UploadFile = File(...)):
if not file.filename:
return JSONResponse(content={"error": "No file selected."}, status_code=400)
cookies = await get_cookies()
if 'csrftoken' not in cookies or 'sessionid' not in cookies:
return JSONResponse(content={"error": "Failed to obtain necessary cookies"}, status_code=500)
upload_result = await initiate_upload(cookies, file.filename, file.content_type)
if not upload_result or 'upload_url' not in upload_result:
return JSONResponse(content={"error": "Failed to initiate upload"}, status_code=500)
file_content = await file.read()
upload_success = await retry_upload(upload_result['upload_url'], file_content, file.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]}"
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)
async def generate():
for chunk in response.iter_content(chunk_size=8192):
yield chunk
response_headers = {key: value for key, value in response.headers.items()}
response_headers['Access-Control-Allow-Origin'] = '*'
response_headers['Content-Disposition'] = 'inline'
return StreamingResponse(generate(), status_code=response.status_code, headers=response_headers)
@app.get("/embed")
async def embed_video(url: str, thumbnail: str):
html = f'''
<!DOCTYPE html>
<html lang="en">
<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;
width: 100%;
height: 100%;
background: #000;
display: flex;
align-items: center;
justify-content: center;
}}
#video {{
width: 100%;
height: 100%;
}}
.video-container {{
position: relative;
width: 100%;
max-width: 1280px;
height: auto;
}}
.video-container video {{
width: 100%;
height: auto;
}}
</style>
</head>
<body>
<div class="video-container">
<video id="video" controls autoplay>
<source src="{url}" type="video/mp4">
Your browser does not support the video tag.
</video>
</div>
</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'
})
return response.cookies.get_dict()
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'
})
return response.json()
except Exception as e:
print(f'Error initiating upload: {e}')
return {}
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:
for attempt in range(1, max_retries + 1):
success = await upload_file(upload_url, file_content, content_type)
if success:
return True
else:
print(f"Upload failed on attempt {attempt}, retrying in {delay} seconds...")
await asyncio.sleep(delay)
return False