Convert to a classic webapp that serves html pages.
This commit is contained in:
parent
3cde0ad202
commit
2e54447e15
@ -3,7 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<title>File Server</title>
|
<title>Image Server</title>
|
||||||
<style>
|
<style>
|
||||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||||
html, body { height: 100%; overflow: hidden; background: #1a1a1a; }
|
html, body { height: 100%; overflow: hidden; background: #1a1a1a; }
|
||||||
@ -34,71 +34,21 @@
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="container">
|
<div id="container">
|
||||||
<a href="#" id="img-link"><img id="img" alt="Random image"></a>
|
<a href="$next_url"><img id="img" src="$img_url" title="$filename"></a>
|
||||||
<a href="#" class="chevron left" id="prev-btn">‹</a>
|
<a href="$prev_url" class="chevron left" id="prev-btn">‹</a>
|
||||||
<a href="#" class="chevron right" id="next-btn">›</a>
|
<a href="$next_url" class="chevron right" id="next-btn">›</a>
|
||||||
</div>
|
</div>
|
||||||
<script>
|
<script>
|
||||||
let currentData = { img: '$img_hash', next: '$next_hash', previous: '$prev_hash' };
|
|
||||||
|
|
||||||
function loadImageSrc(hash) {
|
|
||||||
document.getElementById('img').src = '/' + hash + '/data';
|
|
||||||
document.getElementById('img-link').href = '#';
|
|
||||||
history.replaceState(null, '', '#' + hash);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function loadInfo(hash) {
|
|
||||||
const response = await fetch('/' + hash);
|
|
||||||
if (!response.ok) {
|
|
||||||
loadRandom();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
currentData = await response.json();
|
|
||||||
loadImageSrc(currentData.img);
|
|
||||||
document.getElementById('img').title = currentData.filename || '';
|
|
||||||
document.getElementById('prev-btn').href = '#' + currentData.previous;
|
|
||||||
document.getElementById('next-btn').href = '#' + currentData.next;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function loadRandom() {
|
|
||||||
const response = await fetch('/random?' + Date.now());
|
|
||||||
const data = await response.json();
|
|
||||||
await loadInfo(data.img);
|
|
||||||
}
|
|
||||||
|
|
||||||
window.addEventListener('hashchange', function() {
|
|
||||||
const hash = window.location.hash.slice(1);
|
|
||||||
if (hash) {
|
|
||||||
loadInfo(hash);
|
|
||||||
} else {
|
|
||||||
loadRandom();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
window.addEventListener('hashchange', function() {
|
|
||||||
const hash = window.location.hash.slice(1);
|
|
||||||
if (hash) loadInfo(hash);
|
|
||||||
});
|
|
||||||
|
|
||||||
document.addEventListener('keydown', function(e) {
|
document.addEventListener('keydown', function(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (e.code === 'Space') {
|
if (e.code === 'Space') {
|
||||||
window.location.hash = '';
|
document.getElementById('next-btn').click();
|
||||||
} else if (e.code === 'ArrowLeft') {
|
} else if (e.code === 'ArrowLeft') {
|
||||||
document.getElementById('prev-btn').click();
|
document.getElementById('prev-btn').click();
|
||||||
} else if (e.code === 'ArrowRight') {
|
} else if (e.code === 'ArrowRight') {
|
||||||
document.getElementById('next-btn').click();
|
document.getElementById('next-btn').click();
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const hash = window.location.hash.slice(1);
|
|
||||||
if (hash) {
|
|
||||||
loadInfo(hash);
|
|
||||||
} else {
|
|
||||||
loadRandom();
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
64
main.py
64
main.py
@ -11,7 +11,7 @@ from io import BytesIO
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from fastapi import FastAPI, HTTPException
|
from fastapi import FastAPI, HTTPException
|
||||||
from fastapi.responses import FileResponse, HTMLResponse, StreamingResponse
|
from fastapi.responses import FileResponse, HTMLResponse, RedirectResponse, StreamingResponse
|
||||||
|
|
||||||
app = FastAPI()
|
app = FastAPI()
|
||||||
file_mapping = {}
|
file_mapping = {}
|
||||||
@ -124,42 +124,43 @@ def initialize_server(args: argparse.Namespace):
|
|||||||
|
|
||||||
@app.get("/")
|
@app.get("/")
|
||||||
async def root():
|
async def root():
|
||||||
"""Serve the Frontend app with navigation hashes"""
|
"""Redirect to a random file hash"""
|
||||||
if not file_mapping:
|
if not file_mapping:
|
||||||
raise HTTPException(status_code=404, detail="No files indexed")
|
raise HTTPException(status_code=404, detail="No files indexed")
|
||||||
|
|
||||||
keys = list(file_mapping.keys())
|
keys = list(file_mapping.keys())
|
||||||
current = keys[0]
|
random_idx = random.randint(0, len(keys) - 1)
|
||||||
next_hash = keys[1] if len(keys) > 1 else keys[0]
|
return RedirectResponse(url="/{keys}".format(keys=keys[random_idx]))
|
||||||
prev_hash = keys[-1] if len(keys) > 1 else keys[0]
|
|
||||||
|
|
||||||
|
@app.get("/{file_hash}")
|
||||||
|
async def hash_page(file_hash: str):
|
||||||
|
"""Serve a page for a specific file hash with navigation"""
|
||||||
|
if file_hash not in file_mapping:
|
||||||
|
raise HTTPException(status_code=404, detail="File not found")
|
||||||
|
|
||||||
|
keys = list(file_mapping.keys())
|
||||||
|
idx = keys.index(file_hash)
|
||||||
|
next_hash = keys[(idx + 1) % len(keys)]
|
||||||
|
prev_hash = keys[idx - 1] if idx > 0 else keys[-1]
|
||||||
|
|
||||||
|
indexer = _find_indexer_for_hash(file_hash)
|
||||||
|
filename = indexer.get_filename_by_hash(file_hash) if indexer else ""
|
||||||
|
|
||||||
with open("frontend.html", "r") as f:
|
with open("frontend.html", "r") as f:
|
||||||
content = f.read()
|
content = f.read()
|
||||||
|
|
||||||
template = string.Template(content)
|
template = string.Template(content)
|
||||||
content = template.substitute(
|
content = template.substitute(
|
||||||
img_hash=current,
|
img_url="/api/{file_hash}/data".format(file_hash=file_hash),
|
||||||
next_hash=next_hash,
|
next_url="/{next_hash}".format(next_hash=next_hash),
|
||||||
prev_hash=prev_hash,
|
prev_url="/{prev_hash}".format(prev_hash=prev_hash),
|
||||||
|
filename=filename,
|
||||||
)
|
)
|
||||||
|
|
||||||
return HTMLResponse(content=content)
|
return HTMLResponse(content=content)
|
||||||
|
|
||||||
|
|
||||||
@app.get("/api/random")
|
|
||||||
async def get_random_file():
|
|
||||||
"""Get random file hashes from the mapping"""
|
|
||||||
if not file_mapping:
|
|
||||||
raise HTTPException(status_code=404, detail="No files indexed")
|
|
||||||
|
|
||||||
keys = list(file_mapping.keys())
|
|
||||||
random_idx = random.randint(0, len(keys) - 1)
|
|
||||||
current = keys[random_idx]
|
|
||||||
next_hash = keys[(random_idx + 1) % len(keys)]
|
|
||||||
prev_hash = keys[random_idx - 1] if random_idx > 0 else keys[-1]
|
|
||||||
return {"img": current, "next": next_hash, "previous": prev_hash}
|
|
||||||
|
|
||||||
|
|
||||||
def _find_indexer_for_hash(file_hash: str):
|
def _find_indexer_for_hash(file_hash: str):
|
||||||
"""Find the indexer that contains the file with the given hash"""
|
"""Find the indexer that contains the file with the given hash"""
|
||||||
for idx in indexers:
|
for idx in indexers:
|
||||||
@ -192,25 +193,6 @@ async def get_file_data(file_hash: str):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@app.get("/api/{file_hash}")
|
|
||||||
async def get_file_info(file_hash: str):
|
|
||||||
"""Get file info by hash"""
|
|
||||||
if file_hash not in file_mapping:
|
|
||||||
raise HTTPException(status_code=404, detail="File not found")
|
|
||||||
|
|
||||||
keys = list(file_mapping.keys())
|
|
||||||
idx = keys.index(file_hash)
|
|
||||||
next_hash = keys[(idx + 1) % len(keys)]
|
|
||||||
prev_hash = keys[idx - 1] if idx > 0 else keys[-1]
|
|
||||||
|
|
||||||
indexer = _find_indexer_for_hash(file_hash)
|
|
||||||
if not indexer:
|
|
||||||
raise HTTPException(status_code=404, detail="File not found")
|
|
||||||
|
|
||||||
filename = indexer.get_filename_by_hash(file_hash)
|
|
||||||
return {"img": file_hash, "next": next_hash, "previous": prev_hash, "filename": filename}
|
|
||||||
|
|
||||||
|
|
||||||
# Optional: Add a health check endpoint
|
# Optional: Add a health check endpoint
|
||||||
@app.get("/api/health")
|
@app.get("/api/health")
|
||||||
async def health_check():
|
async def health_check():
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user