Initial commit — Movie Night media discovery app

AI-powered web app that recommends unwatched movies from a Jellyfin
library based on natural language mood input. Jellyfin auth, modular
LLM backend (Claude/OpenAI/Ollama), two-tier pre-filter + AI ranking,
mobile-responsive dark theme UI with poster cards and deep links.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-14 19:20:56 -07:00
commit 3d5de06b44
30 changed files with 1881 additions and 0 deletions
+50
View File
@@ -0,0 +1,50 @@
import json
import logging
from openai import AsyncOpenAI
from app.config import settings
from app.models import Movie
from app.services.llm.base import SYSTEM_PROMPT, LLMProvider, build_user_message
logger = logging.getLogger("movie-night.llm.ollama")
class OllamaProvider(LLMProvider):
def __init__(self):
base_url = settings.llm_base_url or "http://localhost:11434/v1"
self.client = AsyncOpenAI(api_key="ollama", base_url=base_url)
self.model = settings.llm_model or "llama3"
async def get_recommendations(self, mood: str, candidates: list[Movie], max_results: int = 6) -> list[dict]:
system = SYSTEM_PROMPT.format(max_results=max_results)
user_msg = build_user_message(mood, candidates)
logger.info(f"Calling Ollama ({self.model}) with {len(candidates)} candidates")
response = await self.client.chat.completions.create(
model=self.model,
max_tokens=2048,
messages=[
{"role": "system", "content": system},
{"role": "user", "content": user_msg},
],
)
text = response.choices[0].message.content.strip()
try:
data = json.loads(text)
return data.get("recommendations", [])
except json.JSONDecodeError:
# Try to extract JSON from response
start = text.find("{")
end = text.rfind("}") + 1
if start >= 0 and end > start:
try:
data = json.loads(text[start:end])
return data.get("recommendations", [])
except json.JSONDecodeError:
pass
logger.error(f"Failed to parse Ollama response: {text[:200]}")
return []