Compare commits

..

No commits in common. "7b96ca27d8320154c42016d1cac1a01bac973317" and "3cde0ad2024f21f7c0481fa9dcc914de9b73f7ae" have entirely different histories.

3 changed files with 96 additions and 53 deletions

View File

@ -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>Image Server</title> <title>File 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,21 +34,71 @@
</head> </head>
<body> <body>
<div id="container"> <div id="container">
<a href="$next_url"><img id="img" src="$img_url" title="$filename"></a> <a href="#" id="img-link"><img id="img" alt="Random image"></a>
<a href="$prev_url" class="chevron left" id="prev-btn">&#8249;</a> <a href="#" class="chevron left" id="prev-btn">&#8249;</a>
<a href="$next_url" class="chevron right" id="next-btn">&#8250;</a> <a href="#" class="chevron right" id="next-btn">&#8250;</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') {
document.getElementById('next-btn').click(); window.location.hash = '';
} 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
View File

@ -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, RedirectResponse, StreamingResponse from fastapi.responses import FileResponse, HTMLResponse, StreamingResponse
app = FastAPI() app = FastAPI()
file_mapping = {} file_mapping = {}
@ -124,43 +124,42 @@ def initialize_server(args: argparse.Namespace):
@app.get("/") @app.get("/")
async def root(): async def root():
"""Redirect to a random file hash""" """Serve the Frontend app with navigation hashes"""
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())
random_idx = random.randint(0, len(keys) - 1) current = keys[0]
return RedirectResponse(url="/{keys}".format(keys=keys[random_idx])) next_hash = keys[1] if len(keys) > 1 else keys[0]
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_url="/api/{file_hash}/data".format(file_hash=file_hash), img_hash=current,
next_url="/{next_hash}".format(next_hash=next_hash), next_hash=next_hash,
prev_url="/{prev_hash}".format(prev_hash=prev_hash), 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:
@ -193,6 +192,25 @@ 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():

View File

@ -1,25 +0,0 @@
{
"$schema": "https://opencode.ai/config.json",
"provider": {
"ollama": {
"npm": "@ai-sdk/openai-compatible",
"name": "Local Llama-server",
"options": {
"baseURL": "http://llamaserver:8080/v1"
},
"models": {
"Qwen3.5-35B-A3B": {
"name": "Qwen3.5 35B-A3B"
}
}
},
"aws": {
"options": {
"accessKeyId": "{env:AWS_ACCESS_KEY_ID}",
"secretAccessKey": "{env:AWS_SECRET_ACCESS_KEY}",
"region": "us-east-1"
}
}
}
}