fileuploader / app.py
coollsd's picture
Update app.py
fdea472 verified
raw
history blame
35.9 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 aiohttp
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 {
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;
}
.history-btn-container {
display: flex;
justify-content: center;
gap: 10px;
margin-top: 1rem;
}
.history-btn, .clear-history-btn {
display: inline-block;
padding: 8px 16px;
font-size: 0.9rem;
font-weight: 500;
color: #ffffff;
border: none;
border-radius: 3px;
cursor: pointer;
transition: background-color 0.3s ease, box-shadow 0.3s ease;
}
.history-btn {
background-color: #2a2a2a;
}
.history-btn:hover {
background-color: #3a3a3a;
box-shadow: 0 0 10px rgba(200, 200, 200, 0.3);
}
.clear-history-btn {
background-color: #1a0505;
}
.clear-history-btn:hover {
background-color: #2a0a0a;
box-shadow: 0 0 10px rgba(255, 0, 0, 0.3);
}
.history-modal {
display: none;
position: fixed;
z-index: 3;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: auto;
background-color: rgba(0,0,0,0.8);
}
.history-modal-content {
background-color: #1e1e1e;
margin: 10% auto;
padding: 20px;
border: 1px solid #333;
width: 90%;
max-width: 600px;
border-radius: 10px;
color: #e0e0e0;
animation: modalFadeIn 0.3s;
position: relative;
max-height: 80vh;
overflow-y: auto;
}
.history-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px;
border-bottom: 1px solid #333;
transition: background-color 0.3s ease;
}
.history-item:hover {
background-color: #2a2a2a;
}
.history-item-name {
flex-grow: 1;
margin-right: 10px;
word-break: break-all;
}
.history-item-actions {
display: flex;
gap: 5px;
}
.quick-open-modal {
display: none;
position: fixed;
z-index: 4;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: rgba(0,0,0,0.9);
overflow: auto;
}
.quick-open-content {
margin: 5% auto;
padding: 20px;
width: 90%;
max-width: 800px;
text-align: center;
}
.quick-open-content img,
.quick-open-content video,
.quick-open-content audio {
max-width: 100%;
max-height: 70vh;
margin-bottom: 20px;
}
.quick-open-content iframe {
width: 100%;
height: 70vh;
border: none;
}
@media (max-width: 480px) {
.container {
padding: 1.5rem;
}
h1 {
font-size: 1.5rem;
}
.btn, .small-btn {
font-size: 0.9rem;
padding: 10px 20px;
margin: 0.25rem;
}
.file-types {
font-size: 0.7rem;
}
.modal-content, .history-modal-content {
width: 95%;
margin: 5% auto;
padding: 15px;
}
.history-item {
flex-direction: column;
align-items: flex-start;
}
.history-item-actions {
margin-top: 10px;
flex-wrap: wrap;
}
.history-item-name {
font-size: 0.9rem;
}
.drop-zone {
padding: 15px;
}
.drop-zone p {
font-size: 0.9rem;
}
.result-link {
font-size: 0.9rem;
}
.link-buttons {
flex-direction: column;
align-items: stretch;
}
.link-buttons .small-btn {
margin: 0.25rem 0;
}
.close {
top: 5px;
right: 10px;
font-size: 24px;
}
.quick-open-content {
margin: 10% auto;
padding: 10px;
}
}
</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 class="history-btn-container">
<button id="historyBtn" class="history-btn">View Upload History</button>
<button id="clearHistoryBtn" class="clear-history-btn">Clear History</button>
</div>
</div>
<div id="embedModal" class="modal">
<div class="modal-content">
<span class="close">&times;</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>
<div id="historyModal" class="history-modal">
<div class="history-modal-content">
<span class="close">&times;</span>
<h2>Upload History</h2>
<div id="historyList"></div>
</div>
</div>
<div id="quickOpenModal" class="quick-open-modal">
<div class="quick-open-content">
<span class="close">&times;</span>
<div id="quickOpenContent"></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 historyModal = document.getElementById('historyModal');
const quickOpenModal = document.getElementById('quickOpenModal');
const span = document.getElementsByClassName("close");
const embedLinkInput = document.getElementById('embedLink');
const uploadBtn = document.getElementById('uploadBtn');
const historyBtn = document.getElementById('historyBtn');
const clearHistoryBtn = document.getElementById('clearHistoryBtn');
const historyList = document.getElementById('historyList');
const quickOpenContent = document.getElementById('quickOpenContent');
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[0].onclick = function() {
modal.style.display = "none";
}
span[1].onclick = function() {
historyModal.style.display = "none";
}
span[2].onclick = function() {
quickOpenModal.style.display = "none";
}
window.onclick = function(event) {
if (event.target == modal) {
modal.style.display = "none";
}
if (event.target == historyModal) {
historyModal.style.display = "none";
}
if (event.target == quickOpenModal) {
quickOpenModal.style.display = "none";
}
}
historyBtn.onclick = function() {
showHistory();
}
clearHistoryBtn.onclick = function() {
if (confirm('Are you sure you want to clear your upload history?')) {
localStorage.removeItem('uploadHistory');
alert('Upload history has been cleared.');
}
}
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) {
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);
const formData = new FormData();
formData.append('file', file);
try {
const response = await fetch('/upload', {
method: 'POST',
body: formData
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const result = await response.json();
if (result.url) {
addResultLink(result.url, file.name);
saveToHistory(file.name, result.url);
} else {
throw new Error('Upload failed: ' + result.error);
}
} catch (error) {
console.error('Upload error:', error);
alert('Upload failed. Please try again.');
} finally {
resetUploadState();
}
}
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';
progressContainer.style.display = 'none';
}
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!');
}
function saveToHistory(fileName, url) {
let history = JSON.parse(localStorage.getItem('uploadHistory')) || [];
history.unshift({ fileName, url, timestamp: new Date().toISOString() });
if (history.length > 500) history = history.slice(0, 500);
localStorage.setItem('uploadHistory', JSON.stringify(history));
}
function showHistory() {
const history = JSON.parse(localStorage.getItem('uploadHistory')) || [];
historyList.innerHTML = '';
history.forEach(item => {
const historyItem = document.createElement('div');
historyItem.className = 'history-item';
const itemName = document.createElement('span');
itemName.className = 'history-item-name';
itemName.textContent = item.fileName;
historyItem.appendChild(itemName);
const actionsContainer = document.createElement('div');
actionsContainer.className = 'history-item-actions';
const copyBtn = document.createElement('button');
copyBtn.textContent = 'Copy Link';
copyBtn.className = 'small-btn';
copyBtn.onclick = () => {
navigator.clipboard.writeText(window.location.origin + item.url).then(() => {
alert('Link copied to clipboard!');
});
};
actionsContainer.appendChild(copyBtn);
const openBtn = document.createElement('button');
openBtn.textContent = 'Open';
openBtn.className = 'small-btn';
openBtn.onclick = () => {
window.open(window.location.origin + item.url, '_blank');
};
actionsContainer.appendChild(openBtn);
const quickOpenBtn = document.createElement('button');
quickOpenBtn.textContent = 'Quick Open';
quickOpenBtn.className = 'small-btn';
quickOpenBtn.onclick = () => {
quickOpen(item.url, item.fileName);
};
actionsContainer.appendChild(quickOpenBtn);
if (item.fileName.toLowerCase().endsWith('.mp4')) {
const embedBtn = document.createElement('button');
embedBtn.textContent = 'Embed';
embedBtn.className = 'small-btn';
embedBtn.onclick = () => {
showEmbedModal(item.url);
historyModal.style.display = "none";
};
actionsContainer.appendChild(embedBtn);
}
historyItem.appendChild(actionsContainer);
historyList.appendChild(historyItem);
});
historyModal.style.display = "block";
}
function quickOpen(url, fileName) {
quickOpenContent.innerHTML = '';
const fullUrl = window.location.origin + url;
if (fileName.match(/\.(jpeg|jpg|gif|png)$/i)) {
const img = document.createElement('img');
img.src = fullUrl;
img.alt = fileName;
quickOpenContent.appendChild(img);
} else if (fileName.toLowerCase().endsWith('.mp4')) {
const video = document.createElement('video');
video.src = fullUrl;
video.controls = true;
quickOpenContent.appendChild(video);
} else if (fileName.toLowerCase().endsWith('.mp3')) {
const audio = document.createElement('audio');
audio.src = fullUrl;
audio.controls = true;
quickOpenContent.appendChild(audio);
} else if (fileName.toLowerCase().endsWith('.pdf')) {
const iframe = document.createElement('iframe');
iframe.src = fullUrl;
quickOpenContent.appendChild(iframe);
} else if (fileName.toLowerCase().endsWith('.txt')) {
fetch(fullUrl)
.then(response => response.text())
.then(text => {
const pre = document.createElement('pre');
pre.textContent = text;
quickOpenContent.appendChild(pre);
});
} else {
const link = document.createElement('a');
link.href = fullUrl;
link.textContent = 'Download ' + fileName;
link.target = '_blank';
quickOpenContent.appendChild(link);
}
quickOpenModal.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 get 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)
upload_success = await chunked_upload(upload_result['upload_url'], file, file.content_type)
if not upload_success:
return JSONResponse(content={"error": "Failed to upload file 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 {}
async with aiohttp.ClientSession() as session:
async with session.get(original_url, headers=headers) as response:
content = await response.read()
headers = dict(response.headers)
headers['Access-Control-Allow-Origin'] = '*'
headers['Content-Disposition'] = 'inline'
if response.status == 206:
headers['Content-Range'] = response.headers.get('Content-Range')
return StreamingResponse(content, status_code=response.status, 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]:
async with aiohttp.ClientSession() as session:
async with session.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'
}) as response:
return dict(response.cookies)
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}'
async with aiohttp.ClientSession() as session:
async with session.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'
}) as response:
return await response.json()
async def chunked_upload(upload_url: str, file: UploadFile, content_type: str, chunk_size: int = 1024 * 1024) -> bool:
async with aiohttp.ClientSession() as session:
file_size = await file.seek(0, 2)
await file.seek(0)
for start in range(0, file_size, chunk_size):
end = min(start + chunk_size, file_size)
chunk = await file.read(end - start)
headers = {
'Content-Type': content_type,
'Content-Range': f'bytes {start}-{end-1}/{file_size}'
}
for attempt in range(5): # 5 retries
try:
async with session.put(upload_url, data=chunk, headers=headers) as response:
if response.status == 200:
break
except aiohttp.ClientError:
if attempt == 4: # Last attempt
return False
await asyncio.sleep(2 ** attempt) # Exponential backoff
else:
return False
return True
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=7860)