|
from fastapi import FastAPI, File, UploadFile, Request |
|
from fastapi.responses import HTMLResponse, JSONResponse, StreamingResponse, FileResponse |
|
import requests |
|
import time |
|
import asyncio |
|
from typing import Dict |
|
import os |
|
import mimetypes |
|
|
|
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>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>PRO Uploader</h1> |
|
<form id="uploadForm"> |
|
<div id="dropZone" class="drop-zone"> |
|
<input type="file" name="file" id="file" class="file-input" accept="*" 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"> |
|
All file types are supported |
|
</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">×</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">×</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">×</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); |
|
|
|
while (true) { |
|
try { |
|
const xhr = new XMLHttpRequest(); |
|
xhr.open('POST', '/upload', true); |
|
xhr.upload.onprogress = (event) => updateProgress(event, progressBar.querySelector('.progress')); |
|
|
|
xhr.onload = function() { |
|
if (xhr.status === 200) { |
|
const response = JSON.parse(xhr.responseText); |
|
if (response.url) { |
|
addResultLink(response.url, file.name, response.originalExtension); |
|
saveToHistory(file.name, response.url, response.originalExtension); |
|
resetUploadState(); |
|
return; |
|
} else { |
|
throw new Error('Upload failed: ' + response.error); |
|
} |
|
} else { |
|
throw new Error(`HTTP error! status: ${xhr.status}`); |
|
} |
|
}; |
|
|
|
xhr.onerror = function() { |
|
throw new Error('Network error occurred'); |
|
}; |
|
|
|
xhr.send(formData); |
|
|
|
await new Promise((resolve, reject) => { |
|
xhr.onloadend = resolve; |
|
xhr.onerror = reject; |
|
}); |
|
|
|
break; |
|
} catch (error) { |
|
console.error('Upload error:', error); |
|
await new Promise(resolve => setTimeout(resolve, 1000)); |
|
} |
|
} |
|
} |
|
|
|
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 updateProgress(event, progressBar) { |
|
if (event.lengthComputable) { |
|
const percentComplete = (event.loaded / event.total) * 100; |
|
progressBar.style.width = percentComplete + '%'; |
|
} |
|
} |
|
|
|
function resetUploadState() { |
|
fileInput.value = ''; |
|
fileName.textContent = ''; |
|
uploadBtn.style.display = 'none'; |
|
uploadBtn.disabled = false; |
|
loadingSpinner.style.display = 'none'; |
|
} |
|
|
|
function addResultLink(url, fileName, originalExtension) { |
|
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'; |
|
link.download = fileName; |
|
|
|
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); |
|
|
|
const openBtn = document.createElement('button'); |
|
openBtn.textContent = 'Open'; |
|
openBtn.className = 'small-btn'; |
|
openBtn.onclick = () => { |
|
quickOpen(url, fileName, originalExtension); |
|
}; |
|
buttonsContainer.appendChild(openBtn); |
|
|
|
if (originalExtension.toLowerCase() === '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 = `https://x266.mov/e/${encodeURIComponent(window.location.origin + url)}`; |
|
embedLinkInput.value = embedUrl; |
|
modal.style.display = "block"; |
|
} |
|
|
|
function copyEmbedLink() { |
|
embedLinkInput.select(); |
|
document.execCommand('copy'); |
|
alert('Embed link copied to clipboard!'); |
|
} |
|
|
|
function saveToHistory(fileName, url, originalExtension) { |
|
let history = JSON.parse(localStorage.getItem('uploadHistory')) || []; |
|
history.unshift({ fileName, url, originalExtension, 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 = () => { |
|
quickOpen(item.url, item.fileName, item.originalExtension); |
|
}; |
|
actionsContainer.appendChild(openBtn); |
|
|
|
if (item.originalExtension.toLowerCase() === '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, originalExtension) { |
|
quickOpenContent.innerHTML = ''; |
|
const fullUrl = window.location.origin + url; |
|
|
|
if (['jpeg', 'jpg', 'gif', 'png'].includes(originalExtension.toLowerCase())) { |
|
const img = document.createElement('img'); |
|
img.src = fullUrl; |
|
img.alt = fileName; |
|
quickOpenContent.appendChild(img); |
|
} else if (originalExtension.toLowerCase() === 'mp4') { |
|
const video = document.createElement('video'); |
|
video.src = fullUrl; |
|
video.controls = true; |
|
quickOpenContent.appendChild(video); |
|
} else if (originalExtension.toLowerCase() === 'mp3') { |
|
const audio = document.createElement('audio'); |
|
audio.src = fullUrl; |
|
audio.controls = true; |
|
quickOpenContent.appendChild(audio); |
|
} else if (originalExtension.toLowerCase() === 'pdf') { |
|
const iframe = document.createElement('iframe'); |
|
iframe.src = fullUrl; |
|
quickOpenContent.appendChild(iframe); |
|
} else if (originalExtension.toLowerCase() === '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'; |
|
link.download = fileName; |
|
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"}, status_code=500) |
|
|
|
original_extension = os.path.splitext(file.filename)[1][1:] |
|
supported_types = ['mp4', 'png', 'jpg', 'jpeg', 'gif', 'mp3', 'pdf', 'txt'] |
|
|
|
if original_extension.lower() in supported_types: |
|
temp_filename = file.filename |
|
content_type = file.content_type |
|
else: |
|
temp_filename = f"{file.filename}.png" |
|
content_type = "image/png" |
|
|
|
upload_result = await initiate_upload(cookies, temp_filename, content_type) |
|
if not upload_result or 'upload_url' not in upload_result: |
|
return JSONResponse(content={"error": "Failed to upload"}, status_code=500) |
|
|
|
file_content = await file.read() |
|
upload_success = await retry_upload(upload_result['upload_url'], file_content, content_type) |
|
if not upload_success: |
|
return JSONResponse(content={"error": "FAILED GOD MAN AFTER alot of attempts"}, status_code=500) |
|
|
|
original_url = upload_result['serving_url'] |
|
mirrored_url = f"/upload/{original_url.split('/pbxt/')[1]}" |
|
|
|
if original_extension.lower() not in supported_types: |
|
mirrored_url = mirrored_url.replace('.png', '') |
|
|
|
return JSONResponse(content={"url": mirrored_url, "originalExtension": original_extension}) |
|
|
|
@app.get("/upload/{path:path}") |
|
async def handle_file_stream(path: str, request: Request): |
|
original_url = f'https://replicate.delivery/pbxt/{path}' |
|
|
|
if not path.lower().endswith(('.png', '.jpg', '.jpeg', '.gif', '.mp4', '.mp3', '.pdf', '.txt')): |
|
original_url += '.png' |
|
|
|
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') |
|
|
|
original_extension = os.path.splitext(path)[1][1:] |
|
content_type, _ = mimetypes.guess_type(f"file.{original_extension}") |
|
if content_type: |
|
headers['Content-Type'] = content_type |
|
|
|
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: |
|
while True: |
|
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) |
|
|
|
return False |
|
|