Compare commits

...

2 Commits

3 changed files with 53 additions and 96 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>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">&#8249;</a> <a href="$prev_url" class="chevron left" id="prev-btn">&#8249;</a>
<a href="#" class="chevron right" id="next-btn">&#8250;</a> <a href="$next_url" 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') {
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
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, 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():

25
opencode.json Normal file
View File

@ -0,0 +1,25 @@
{
"$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"
}
}
}
}