phantomnode-api / app /server.py
tripathiji1312's picture
Fix root and double-slash 404 routing errors
92d9755
Raw
History Blame Contribute Delete
4.23 kB
from fastapi import FastAPI, UploadFile, File
from fastapi.responses import FileResponse, JSONResponse
from fastapi.middleware.cors import CORSMiddleware
import os
import sys
import shutil
import logging
import tempfile
import subprocess
# Add the app directory to PYTHONPATH so local imports work
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
import conversion
import audio_work
logging.basicConfig(level=logging.INFO)
app = FastAPI(title="PhantomNode API")
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
@app.get("/")
@app.get("/health")
@app.get("//health") # Handle trailing slash edgecases from frontend env vars
async def health():
return {"status": "ok", "app": "PhantomNode API"}
@app.post("/tx")
async def transmit_file(file: UploadFile = File(...)):
"""
Accept a text file → encode to binary → generate FSK audio WAV.
Returns the WAV file for the browser to play through speakers.
"""
try:
os.makedirs('tests', exist_ok=True)
filepath = f"tests/{file.filename}"
with open(filepath, "wb") as buffer:
shutil.copyfileobj(file.file, buffer)
# Encode to binary (with Hamming ECC)
logging.info("Encoding %s to binary...", file.filename)
binary_content = conversion.toBinary(filepath, save=True)
# Generate audio WAV
logging.info("Generating acoustic waveform...")
wav_path = audio_work.generateAudio(binary_content, filename="api_out")
logging.info("Transmission WAV ready: %s", wav_path)
return FileResponse(
wav_path,
media_type="audio/wav",
filename="phantomnode_transmission.wav"
)
except Exception as e:
logging.error("TX error: %s", e)
return JSONResponse(
status_code=500,
content={"error": str(e)}
)
@app.post("/rx")
async def receive_audio(file: UploadFile = File(...)):
"""
Accept a WAV file recorded from the browser's microphone.
Decode the FSK audio back into the original text content.
Returns the decoded text.
"""
try:
# Save uploaded audio (could be WebM, Ogg, etc. from browser MediaRecorder)
with tempfile.NamedTemporaryFile(suffix=".webm", delete=False) as tmp:
shutil.copyfileobj(file.file, tmp)
raw_path = tmp.name
# Convert to WAV (44100Hz mono) using ffmpeg
wav_path = raw_path.replace(".webm", ".wav")
result = subprocess.run(
["ffmpeg", "-y", "-i", raw_path, "-ar", "44100", "-ac", "1", "-f", "wav", wav_path],
capture_output=True, text=True
)
os.unlink(raw_path) # Remove the original
if result.returncode != 0:
raise RuntimeError(f"ffmpeg conversion failed: {result.stderr[:200]}")
logging.info("Converted audio to WAV: %s", wav_path)
# Decode audio → binary string
output_bin = audio_work.readAudioFromFile(wav_path)
# Save binary for conversion
os.makedirs('tests_output', exist_ok=True)
bin_path = 'tests_output/binary_content.txt'
with open(bin_path, 'w') as f:
f.write(output_bin)
# Decode binary → original text
decoded_data = conversion.toString(bin_path)
decoded_text = decoded_data.decode('utf-8', errors='ignore')
logging.info("Decoded payload: %s", decoded_text[:100])
# Clean up
os.unlink(wav_path)
return {
"success": True,
"decoded_text": decoded_text,
"filename": "decoded_payload.txt"
}
except Exception as e:
logging.error("RX error: %s", e)
# Clean up on error
for p in ['raw_path', 'wav_path']:
if p in locals():
try:
os.unlink(locals()[p])
except OSError:
pass
return JSONResponse(
status_code=500,
content={"error": str(e)}
)
if __name__ == "__main__":
import uvicorn
uvicorn.run("server:app", host="0.0.0.0", port=8000, reload=True)