Update app.py
Browse files
app.py
CHANGED
|
@@ -9,7 +9,7 @@ import mimetypes
|
|
| 9 |
import httpx
|
| 10 |
import io
|
| 11 |
import zipfile
|
| 12 |
-
import
|
| 13 |
|
| 14 |
app = FastAPI()
|
| 15 |
|
|
@@ -64,57 +64,65 @@ async def index():
|
|
| 64 |
<meta charset="UTF-8">
|
| 65 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 66 |
<title>File Upload</title>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 67 |
</head>
|
| 68 |
<body>
|
| 69 |
<h1>File Upload & Album Management</h1>
|
| 70 |
-
<form action="/
|
| 71 |
-
<h2>Single File Upload</h2>
|
| 72 |
-
<input type="file" name="file" accept="*/*" required><br><br>
|
| 73 |
-
<button type="submit">Upload</button>
|
| 74 |
-
</form>
|
| 75 |
-
<form action="/album/create" method="post" enctype="multipart/form-data">
|
| 76 |
-
<h2>Create Album</h2>
|
| 77 |
<input type="text" name="album_name" placeholder="Album Name" required><br><br>
|
| 78 |
<input type="file" name="files" accept="*/*" multiple required><br><br>
|
|
|
|
|
|
|
|
|
|
| 79 |
<button type="submit">Create Album</button>
|
| 80 |
</form>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 81 |
<h2>Search Albums</h2>
|
| 82 |
<form action="/search" method="get">
|
| 83 |
<input type="text" name="query" placeholder="Search by album name..." required>
|
| 84 |
<button type="submit">Search</button>
|
| 85 |
</form>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 86 |
</body>
|
| 87 |
</html>
|
| 88 |
"""
|
| 89 |
|
| 90 |
-
@app.post("/
|
| 91 |
-
async def
|
| 92 |
-
|
| 93 |
-
upload_result = await initiate_upload(cookies, file.filename, file.content_type)
|
| 94 |
-
|
| 95 |
-
if upload_result and 'upload_url' in upload_result:
|
| 96 |
-
start_time = time.time()
|
| 97 |
-
file_size = 0
|
| 98 |
-
|
| 99 |
-
async def generate():
|
| 100 |
-
nonlocal file_size
|
| 101 |
-
while chunk := await file.read(8192):
|
| 102 |
-
file_size += len(chunk)
|
| 103 |
-
yield chunk
|
| 104 |
-
|
| 105 |
-
upload_success = await retry_upload_streaming(upload_result['upload_url'], generate(), file.content_type)
|
| 106 |
-
if upload_success:
|
| 107 |
-
serving_path = upload_result['serving_url'].split('/pbxt/')[1]
|
| 108 |
-
elapsed_time = time.time() - start_time
|
| 109 |
-
return {
|
| 110 |
-
"success": True,
|
| 111 |
-
"message": "File uploaded successfully",
|
| 112 |
-
"file_url": f"/upload/{serving_path}",
|
| 113 |
-
"file_size": file_size,
|
| 114 |
-
"elapsed_time": f"{elapsed_time:.2f} seconds"
|
| 115 |
-
}
|
| 116 |
-
|
| 117 |
-
return {"error": "File upload failed"}
|
| 118 |
|
| 119 |
@app.post("/album/create")
|
| 120 |
async def create_album(
|
|
@@ -126,32 +134,26 @@ async def create_album(
|
|
| 126 |
album_files = []
|
| 127 |
|
| 128 |
cookies = await get_cookies()
|
|
|
|
|
|
|
| 129 |
|
| 130 |
for file in files:
|
| 131 |
-
|
| 132 |
-
file_size = 0
|
| 133 |
-
|
| 134 |
-
async def generate():
|
| 135 |
-
nonlocal file_size
|
| 136 |
-
while chunk := await file.read(8192):
|
| 137 |
-
file_size += len(chunk)
|
| 138 |
-
yield chunk
|
| 139 |
-
|
| 140 |
upload_result = await initiate_upload(cookies, file.filename, file.content_type)
|
| 141 |
|
| 142 |
if upload_result and 'upload_url' in upload_result:
|
| 143 |
-
upload_success = await
|
| 144 |
if upload_success:
|
| 145 |
serving_path = upload_result['serving_url'].split('/pbxt/')[1]
|
| 146 |
-
elapsed_time = time.time() - start_time
|
| 147 |
album_files.append({
|
| 148 |
'filename': file.filename,
|
| 149 |
'path': serving_path,
|
| 150 |
'content_type': file.content_type,
|
| 151 |
-
'uploaded_at': datetime.now().isoformat()
|
| 152 |
-
'file_size': file_size,
|
| 153 |
-
'elapsed_time': f"{elapsed_time:.2f} seconds"
|
| 154 |
})
|
|
|
|
|
|
|
|
|
|
| 155 |
|
| 156 |
# Save to Deno KV database
|
| 157 |
await save_album_to_db(album_name, album_id, album_files)
|
|
@@ -198,8 +200,6 @@ async def view_album(album_id: str):
|
|
| 198 |
<div class="file-item">
|
| 199 |
{preview_html}
|
| 200 |
<p>{file['filename']}</p>
|
| 201 |
-
<p>Size: {file['file_size']} bytes</p>
|
| 202 |
-
<p>Upload Time: {file['elapsed_time']}</p>
|
| 203 |
<a href="{file_url}" class="button" onclick="openModal('{file_url}'); return false;">View</a>
|
| 204 |
<a href="{file_url}?download=true" class="button" target="_blank">Download</a>
|
| 205 |
</div>
|
|
@@ -316,11 +316,11 @@ async def initiate_upload(cookies, filename, content_type):
|
|
| 316 |
response = requests.post(url, cookies=cookies, headers=headers)
|
| 317 |
return response.json()
|
| 318 |
|
| 319 |
-
async def
|
| 320 |
delay = 1
|
| 321 |
for _ in range(max_retries):
|
| 322 |
try:
|
| 323 |
-
response = requests.put(upload_url, data=
|
| 324 |
if response.status_code == 200:
|
| 325 |
return True
|
| 326 |
except Exception:
|
|
|
|
| 9 |
import httpx
|
| 10 |
import io
|
| 11 |
import zipfile
|
| 12 |
+
import math
|
| 13 |
|
| 14 |
app = FastAPI()
|
| 15 |
|
|
|
|
| 64 |
<meta charset="UTF-8">
|
| 65 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 66 |
<title>File Upload</title>
|
| 67 |
+
<style>
|
| 68 |
+
.progress-bar {
|
| 69 |
+
width: 100%;
|
| 70 |
+
background-color: #f3f3f3;
|
| 71 |
+
border-radius: 5px;
|
| 72 |
+
margin-top: 10px;
|
| 73 |
+
}
|
| 74 |
+
.progress-bar-inner {
|
| 75 |
+
width: 0%;
|
| 76 |
+
height: 30px;
|
| 77 |
+
background-color: #4caf50;
|
| 78 |
+
border-radius: 5px;
|
| 79 |
+
text-align: center;
|
| 80 |
+
line-height: 30px;
|
| 81 |
+
color: white;
|
| 82 |
+
}
|
| 83 |
+
</style>
|
| 84 |
</head>
|
| 85 |
<body>
|
| 86 |
<h1>File Upload & Album Management</h1>
|
| 87 |
+
<form action="/album/create" method="post" enctype="multipart/form-data" onsubmit="showProgressBar()">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 88 |
<input type="text" name="album_name" placeholder="Album Name" required><br><br>
|
| 89 |
<input type="file" name="files" accept="*/*" multiple required><br><br>
|
| 90 |
+
<div class="progress-bar">
|
| 91 |
+
<div class="progress-bar-inner" id="progressBar">0%</div>
|
| 92 |
+
</div>
|
| 93 |
<button type="submit">Create Album</button>
|
| 94 |
</form>
|
| 95 |
+
<h2>Single File Upload</h2>
|
| 96 |
+
<form action="/single/upload" method="post" enctype="multipart/form-data">
|
| 97 |
+
<input type="file" name="file" accept="*/*" required><br><br>
|
| 98 |
+
<button type="submit">Upload Single File</button>
|
| 99 |
+
</form>
|
| 100 |
<h2>Search Albums</h2>
|
| 101 |
<form action="/search" method="get">
|
| 102 |
<input type="text" name="query" placeholder="Search by album name..." required>
|
| 103 |
<button type="submit">Search</button>
|
| 104 |
</form>
|
| 105 |
+
<script>
|
| 106 |
+
function showProgressBar() {
|
| 107 |
+
const progressBar = document.getElementById('progressBar');
|
| 108 |
+
progressBar.style.width = '0%';
|
| 109 |
+
progressBar.textContent = '0%';
|
| 110 |
+
const interval = setInterval(() => {
|
| 111 |
+
const width = parseInt(progressBar.style.width);
|
| 112 |
+
if (width < 90) {
|
| 113 |
+
progressBar.style.width = (width + 10) + '%';
|
| 114 |
+
progressBar.textContent = (width + 10) + '%';
|
| 115 |
+
}
|
| 116 |
+
}, 500);
|
| 117 |
+
}
|
| 118 |
+
</script>
|
| 119 |
</body>
|
| 120 |
</html>
|
| 121 |
"""
|
| 122 |
|
| 123 |
+
@app.post("/single/upload")
|
| 124 |
+
async def single_upload(file: UploadFile = File(...)):
|
| 125 |
+
return RedirectResponse(url="https://albumup-up1.hf.space/", status_code=303)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 126 |
|
| 127 |
@app.post("/album/create")
|
| 128 |
async def create_album(
|
|
|
|
| 134 |
album_files = []
|
| 135 |
|
| 136 |
cookies = await get_cookies()
|
| 137 |
+
total_files = len(files)
|
| 138 |
+
uploaded_files = 0
|
| 139 |
|
| 140 |
for file in files:
|
| 141 |
+
file_content = await file.read()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 142 |
upload_result = await initiate_upload(cookies, file.filename, file.content_type)
|
| 143 |
|
| 144 |
if upload_result and 'upload_url' in upload_result:
|
| 145 |
+
upload_success = await retry_upload(upload_result['upload_url'], file_content, file.content_type)
|
| 146 |
if upload_success:
|
| 147 |
serving_path = upload_result['serving_url'].split('/pbxt/')[1]
|
|
|
|
| 148 |
album_files.append({
|
| 149 |
'filename': file.filename,
|
| 150 |
'path': serving_path,
|
| 151 |
'content_type': file.content_type,
|
| 152 |
+
'uploaded_at': datetime.now().isoformat()
|
|
|
|
|
|
|
| 153 |
})
|
| 154 |
+
uploaded_files += 1
|
| 155 |
+
progress = math.floor((uploaded_files / total_files) * 100)
|
| 156 |
+
print(f"Upload Progress: {progress}%")
|
| 157 |
|
| 158 |
# Save to Deno KV database
|
| 159 |
await save_album_to_db(album_name, album_id, album_files)
|
|
|
|
| 200 |
<div class="file-item">
|
| 201 |
{preview_html}
|
| 202 |
<p>{file['filename']}</p>
|
|
|
|
|
|
|
| 203 |
<a href="{file_url}" class="button" onclick="openModal('{file_url}'); return false;">View</a>
|
| 204 |
<a href="{file_url}?download=true" class="button" target="_blank">Download</a>
|
| 205 |
</div>
|
|
|
|
| 316 |
response = requests.post(url, cookies=cookies, headers=headers)
|
| 317 |
return response.json()
|
| 318 |
|
| 319 |
+
async def retry_upload(upload_url, file_content, content_type, max_retries=5):
|
| 320 |
delay = 1
|
| 321 |
for _ in range(max_retries):
|
| 322 |
try:
|
| 323 |
+
response = requests.put(upload_url, data=file_content, headers={'Content-Type': content_type})
|
| 324 |
if response.status_code == 200:
|
| 325 |
return True
|
| 326 |
except Exception:
|