Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
@@ -4,6 +4,7 @@ import requests
|
|
4 |
import time
|
5 |
import asyncio
|
6 |
from typing import Dict
|
|
|
7 |
|
8 |
app = FastAPI()
|
9 |
|
@@ -423,7 +424,7 @@ HTML_CONTENT = """
|
|
423 |
gap: 5px;
|
424 |
}
|
425 |
|
426 |
-
|
427 |
display: none;
|
428 |
position: fixed;
|
429 |
z-index: 4;
|
@@ -696,44 +697,28 @@ HTML_CONTENT = """
|
|
696 |
const formData = new FormData();
|
697 |
formData.append('file', file);
|
698 |
|
699 |
-
|
700 |
-
|
701 |
-
|
702 |
-
|
703 |
-
|
704 |
-
|
705 |
-
xhr.onload = function() {
|
706 |
-
if (xhr.status === 200) {
|
707 |
-
const response = JSON.parse(xhr.responseText);
|
708 |
-
if (response.url) {
|
709 |
-
addResultLink(response.url, file.name);
|
710 |
-
saveToHistory(file.name, response.url);
|
711 |
-
resetUploadState();
|
712 |
-
return;
|
713 |
-
} else {
|
714 |
-
throw new Error('Upload failed: ' + response.error);
|
715 |
-
}
|
716 |
-
} else {
|
717 |
-
throw new Error(`HTTP error! status: ${xhr.status}`);
|
718 |
-
}
|
719 |
-
};
|
720 |
-
|
721 |
-
xhr.onerror = function() {
|
722 |
-
throw new Error('Network error occurred');
|
723 |
-
};
|
724 |
-
|
725 |
-
xhr.send(formData);
|
726 |
|
727 |
-
|
728 |
-
|
729 |
-
|
730 |
-
});
|
731 |
|
732 |
-
|
733 |
-
|
734 |
-
|
735 |
-
|
|
|
|
|
736 |
}
|
|
|
|
|
|
|
|
|
|
|
737 |
}
|
738 |
}
|
739 |
|
@@ -753,19 +738,13 @@ HTML_CONTENT = """
|
|
753 |
return container;
|
754 |
}
|
755 |
|
756 |
-
function updateProgress(event, progressBar) {
|
757 |
-
if (event.lengthComputable) {
|
758 |
-
const percentComplete = (event.loaded / event.total) * 100;
|
759 |
-
progressBar.style.width = percentComplete + '%';
|
760 |
-
}
|
761 |
-
}
|
762 |
-
|
763 |
function resetUploadState() {
|
764 |
fileInput.value = '';
|
765 |
fileName.textContent = '';
|
766 |
uploadBtn.style.display = 'none';
|
767 |
uploadBtn.disabled = false;
|
768 |
loadingSpinner.style.display = 'none';
|
|
|
769 |
}
|
770 |
|
771 |
function addResultLink(url, fileName) {
|
@@ -853,7 +832,7 @@ HTML_CONTENT = """
|
|
853 |
};
|
854 |
actionsContainer.appendChild(copyBtn);
|
855 |
|
856 |
-
|
857 |
openBtn.textContent = 'Open';
|
858 |
openBtn.className = 'small-btn';
|
859 |
openBtn.onclick = () => {
|
@@ -869,7 +848,7 @@ HTML_CONTENT = """
|
|
869 |
};
|
870 |
actionsContainer.appendChild(quickOpenBtn);
|
871 |
|
872 |
-
|
873 |
const embedBtn = document.createElement('button');
|
874 |
embedBtn.textContent = 'Embed';
|
875 |
embedBtn.className = 'small-btn';
|
@@ -943,16 +922,15 @@ async def handle_upload(file: UploadFile = File(...)):
|
|
943 |
|
944 |
cookies = await get_cookies()
|
945 |
if 'csrftoken' not in cookies or 'sessionid' not in cookies:
|
946 |
-
return JSONResponse(content={"error": "Failed"}, status_code=500)
|
947 |
|
948 |
upload_result = await initiate_upload(cookies, file.filename, file.content_type)
|
949 |
if not upload_result or 'upload_url' not in upload_result:
|
950 |
-
return JSONResponse(content={"error": "Failed to upload"}, status_code=500)
|
951 |
|
952 |
-
|
953 |
-
upload_success = await retry_upload(upload_result['upload_url'], file_content, file.content_type)
|
954 |
if not upload_success:
|
955 |
-
return JSONResponse(content={"error": "
|
956 |
|
957 |
original_url = upload_result['serving_url']
|
958 |
mirrored_url = f"/rbxg/{original_url.split('/pbxt/')[1]}"
|
@@ -965,20 +943,17 @@ async def handle_video_stream(path: str, request: Request):
|
|
965 |
range_header = request.headers.get('Range')
|
966 |
|
967 |
headers = {'Range': range_header} if range_header else {}
|
968 |
-
|
969 |
-
|
970 |
-
|
971 |
-
|
972 |
-
|
|
|
973 |
|
974 |
-
|
975 |
-
|
976 |
-
headers['Content-Disposition'] = 'inline'
|
977 |
|
978 |
-
|
979 |
-
headers['Content-Range'] = response.headers.get('Content-Range')
|
980 |
-
|
981 |
-
return StreamingResponse(generate(), status_code=response.status_code, headers=headers)
|
982 |
|
983 |
@app.get("/embed")
|
984 |
async def embed_video(url: str, thumbnail: str):
|
@@ -1022,19 +997,16 @@ async def embed_video(url: str, thumbnail: str):
|
|
1022 |
return HTMLResponse(content=html)
|
1023 |
|
1024 |
async def get_cookies() -> Dict[str, str]:
|
1025 |
-
|
1026 |
-
|
1027 |
'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'
|
1028 |
-
})
|
1029 |
-
|
1030 |
-
except Exception as e:
|
1031 |
-
print(f'Error fetching the page: {e}')
|
1032 |
-
return {}
|
1033 |
|
1034 |
async def initiate_upload(cookies: Dict[str, str], filename: str, content_type: str) -> Dict:
|
1035 |
url = f'https://replicate.com/api/upload/{filename}?content_type={content_type}'
|
1036 |
-
|
1037 |
-
|
1038 |
'X-CSRFToken': cookies.get('csrftoken'),
|
1039 |
'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',
|
1040 |
'Referer': 'https://replicate.com/levelsio/neon-tokyo',
|
@@ -1047,31 +1019,37 @@ async def initiate_upload(cookies: Dict[str, str], filename: str, content_type:
|
|
1047 |
'Sec-Fetch-Site': 'same-origin',
|
1048 |
'Sec-GPC': '1',
|
1049 |
'Priority': 'u=1, i'
|
1050 |
-
})
|
1051 |
-
|
1052 |
-
|
1053 |
-
|
1054 |
-
|
1055 |
-
|
1056 |
-
|
1057 |
-
|
1058 |
-
|
1059 |
-
|
1060 |
-
|
1061 |
-
|
1062 |
-
|
1063 |
-
|
1064 |
-
|
1065 |
-
|
1066 |
-
|
1067 |
-
|
1068 |
-
|
1069 |
-
|
1070 |
-
|
1071 |
-
|
1072 |
-
|
1073 |
-
|
1074 |
-
|
1075 |
-
|
1076 |
-
|
1077 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
4 |
import time
|
5 |
import asyncio
|
6 |
from typing import Dict
|
7 |
+
import aiohttp
|
8 |
|
9 |
app = FastAPI()
|
10 |
|
|
|
424 |
gap: 5px;
|
425 |
}
|
426 |
|
427 |
+
.quick-open-modal {
|
428 |
display: none;
|
429 |
position: fixed;
|
430 |
z-index: 4;
|
|
|
697 |
const formData = new FormData();
|
698 |
formData.append('file', file);
|
699 |
|
700 |
+
try {
|
701 |
+
const response = await fetch('/upload', {
|
702 |
+
method: 'POST',
|
703 |
+
body: formData
|
704 |
+
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
705 |
|
706 |
+
if (!response.ok) {
|
707 |
+
throw new Error(`HTTP error! status: ${response.status}`);
|
708 |
+
}
|
|
|
709 |
|
710 |
+
const result = await response.json();
|
711 |
+
if (result.url) {
|
712 |
+
addResultLink(result.url, file.name);
|
713 |
+
saveToHistory(file.name, result.url);
|
714 |
+
} else {
|
715 |
+
throw new Error('Upload failed: ' + result.error);
|
716 |
}
|
717 |
+
} catch (error) {
|
718 |
+
console.error('Upload error:', error);
|
719 |
+
alert('Upload failed. Please try again.');
|
720 |
+
} finally {
|
721 |
+
resetUploadState();
|
722 |
}
|
723 |
}
|
724 |
|
|
|
738 |
return container;
|
739 |
}
|
740 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
741 |
function resetUploadState() {
|
742 |
fileInput.value = '';
|
743 |
fileName.textContent = '';
|
744 |
uploadBtn.style.display = 'none';
|
745 |
uploadBtn.disabled = false;
|
746 |
loadingSpinner.style.display = 'none';
|
747 |
+
progressContainer.style.display = 'none';
|
748 |
}
|
749 |
|
750 |
function addResultLink(url, fileName) {
|
|
|
832 |
};
|
833 |
actionsContainer.appendChild(copyBtn);
|
834 |
|
835 |
+
const openBtn = document.createElement('button');
|
836 |
openBtn.textContent = 'Open';
|
837 |
openBtn.className = 'small-btn';
|
838 |
openBtn.onclick = () => {
|
|
|
848 |
};
|
849 |
actionsContainer.appendChild(quickOpenBtn);
|
850 |
|
851 |
+
if (item.fileName.toLowerCase().endsWith('.mp4')) {
|
852 |
const embedBtn = document.createElement('button');
|
853 |
embedBtn.textContent = 'Embed';
|
854 |
embedBtn.className = 'small-btn';
|
|
|
922 |
|
923 |
cookies = await get_cookies()
|
924 |
if 'csrftoken' not in cookies or 'sessionid' not in cookies:
|
925 |
+
return JSONResponse(content={"error": "Failed to get cookies"}, status_code=500)
|
926 |
|
927 |
upload_result = await initiate_upload(cookies, file.filename, file.content_type)
|
928 |
if not upload_result or 'upload_url' not in upload_result:
|
929 |
+
return JSONResponse(content={"error": "Failed to initiate upload"}, status_code=500)
|
930 |
|
931 |
+
upload_success = await chunked_upload(upload_result['upload_url'], file, file.content_type)
|
|
|
932 |
if not upload_success:
|
933 |
+
return JSONResponse(content={"error": "Failed to upload file after multiple attempts"}, status_code=500)
|
934 |
|
935 |
original_url = upload_result['serving_url']
|
936 |
mirrored_url = f"/rbxg/{original_url.split('/pbxt/')[1]}"
|
|
|
943 |
range_header = request.headers.get('Range')
|
944 |
|
945 |
headers = {'Range': range_header} if range_header else {}
|
946 |
+
async with aiohttp.ClientSession() as session:
|
947 |
+
async with session.get(original_url, headers=headers) as response:
|
948 |
+
content = await response.read()
|
949 |
+
headers = dict(response.headers)
|
950 |
+
headers['Access-Control-Allow-Origin'] = '*'
|
951 |
+
headers['Content-Disposition'] = 'inline'
|
952 |
|
953 |
+
if response.status == 206:
|
954 |
+
headers['Content-Range'] = response.headers.get('Content-Range')
|
|
|
955 |
|
956 |
+
return StreamingResponse(content, status_code=response.status, headers=headers)
|
|
|
|
|
|
|
957 |
|
958 |
@app.get("/embed")
|
959 |
async def embed_video(url: str, thumbnail: str):
|
|
|
997 |
return HTMLResponse(content=html)
|
998 |
|
999 |
async def get_cookies() -> Dict[str, str]:
|
1000 |
+
async with aiohttp.ClientSession() as session:
|
1001 |
+
async with session.get('https://replicate.com/levelsio/neon-tokyo', headers={
|
1002 |
'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'
|
1003 |
+
}) as response:
|
1004 |
+
return dict(response.cookies)
|
|
|
|
|
|
|
1005 |
|
1006 |
async def initiate_upload(cookies: Dict[str, str], filename: str, content_type: str) -> Dict:
|
1007 |
url = f'https://replicate.com/api/upload/{filename}?content_type={content_type}'
|
1008 |
+
async with aiohttp.ClientSession() as session:
|
1009 |
+
async with session.post(url, cookies=cookies, headers={
|
1010 |
'X-CSRFToken': cookies.get('csrftoken'),
|
1011 |
'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',
|
1012 |
'Referer': 'https://replicate.com/levelsio/neon-tokyo',
|
|
|
1019 |
'Sec-Fetch-Site': 'same-origin',
|
1020 |
'Sec-GPC': '1',
|
1021 |
'Priority': 'u=1, i'
|
1022 |
+
}) as response:
|
1023 |
+
return await response.json()
|
1024 |
+
|
1025 |
+
async def chunked_upload(upload_url: str, file: UploadFile, content_type: str, chunk_size: int = 1024 * 1024) -> bool:
|
1026 |
+
async with aiohttp.ClientSession() as session:
|
1027 |
+
file_size = await file.seek(0, 2)
|
1028 |
+
await file.seek(0)
|
1029 |
+
|
1030 |
+
for start in range(0, file_size, chunk_size):
|
1031 |
+
end = min(start + chunk_size, file_size)
|
1032 |
+
chunk = await file.read(end - start)
|
1033 |
+
|
1034 |
+
headers = {
|
1035 |
+
'Content-Type': content_type,
|
1036 |
+
'Content-Range': f'bytes {start}-{end-1}/{file_size}'
|
1037 |
+
}
|
1038 |
+
|
1039 |
+
for attempt in range(5): # 5 retries
|
1040 |
+
try:
|
1041 |
+
async with session.put(upload_url, data=chunk, headers=headers) as response:
|
1042 |
+
if response.status == 200:
|
1043 |
+
break
|
1044 |
+
except aiohttp.ClientError:
|
1045 |
+
if attempt == 4: # Last attempt
|
1046 |
+
return False
|
1047 |
+
await asyncio.sleep(2 ** attempt) # Exponential backoff
|
1048 |
+
else:
|
1049 |
+
return False
|
1050 |
+
|
1051 |
+
return True
|
1052 |
+
|
1053 |
+
if __name__ == "__main__":
|
1054 |
+
import uvicorn
|
1055 |
+
uvicorn.run(app, host="0.0.0.0", port=7860)
|