From e27bb37f7baccebf606c11662f1153fa34b28edd Mon Sep 17 00:00:00 2001 From: Myron Blair Date: Thu, 23 Jul 2026 20:20:29 -0500 Subject: [PATCH] Rebrand StreamHoard, strip Emergent.sh platform artifacts - Remove .emergent/ state dir, tracked .gitconfig, @emergentbase/visual-edits dep + craco wiring, emergent.sh script/meta tags, PostHog telemetry snippet - Rename UI/docs branding Kino -> StreamHoard (README, DEPLOY.md, Navbar, Login, Register, AdminShowEpisodes, FastAPI title, health check response) - Rename docker-compose container/network names to streamhoard-* - Default admin email domain -> admin@streamhoard.local --- .emergent/emergent.yml | 5 -- .gitconfig | 3 - .gitignore | 1 - DEPLOY.md | 22 +++---- README.md | 8 +-- backend/server.py | 6 +- backend/tests/backend_test.py | 12 ++-- backend/tests/test_phase2.py | 6 +- backend/tests/test_phase4_queue.py | 4 +- docker-compose.yml | 26 ++++---- frontend/craco.config.js | 20 ------- frontend/package.json | 1 - frontend/public/index.html | 76 +----------------------- frontend/src/components/Navbar.jsx | 2 +- frontend/src/pages/AdminShowEpisodes.jsx | 2 +- frontend/src/pages/Login.jsx | 4 +- frontend/src/pages/Register.jsx | 4 +- install.sh | 6 +- 18 files changed, 53 insertions(+), 155 deletions(-) delete mode 100644 .emergent/emergent.yml delete mode 100644 .gitconfig diff --git a/.emergent/emergent.yml b/.emergent/emergent.yml deleted file mode 100644 index 1244068..0000000 --- a/.emergent/emergent.yml +++ /dev/null @@ -1,5 +0,0 @@ -{ - "env_image_name": "fastapi_react_mongo_shadcn_base_image_cloud_arm:release-17042026-1", - "job_id": "08d9a7a1-2a0c-4502-9939-fc5d643c04c4", - "created_at": "2026-07-23T22:57:32.873830+00:00Z" -} diff --git a/.gitconfig b/.gitconfig deleted file mode 100644 index 1c1f57c..0000000 --- a/.gitconfig +++ /dev/null @@ -1,3 +0,0 @@ -[user] - email = github@emergent.sh - name = emergent-agent-e1 diff --git a/.gitignore b/.gitignore index 801e859..149105f 100644 --- a/.gitignore +++ b/.gitignore @@ -61,7 +61,6 @@ logs/ # --- Test reports / agent runtime --- test_reports/ -.emergent/ tests/__pycache__/ # --- Misc --- diff --git a/DEPLOY.md b/DEPLOY.md index 51fe98f..f040d57 100644 --- a/DEPLOY.md +++ b/DEPLOY.md @@ -1,8 +1,8 @@ -# Kino — Deployment on a Proxmox LAMP host +# StreamHoard — Deployment on a Proxmox LAMP host -Kino is FastAPI + MongoDB + React. It runs happily alongside your existing +StreamHoard is FastAPI + MongoDB + React. It runs happily alongside your existing LAMP stack (Apache/MySQL/PHP) because we run everything in Docker containers -on their own network. Apache keeps port 80/443, MySQL is untouched, Kino +on their own network. Apache keeps port 80/443, MySQL is untouched, StreamHoard uses whatever port you pick. --- @@ -54,7 +54,7 @@ Log in with the admin credentials from `.env`. ## Running behind your existing Apache (optional, recommended) -If you already have Apache on port 80/443 with virtual hosts, add a Kino vhost +If you already have Apache on port 80/443 with virtual hosts, add a StreamHoard vhost that reverse-proxies to the container. This gives you `https://kino.example.com` instead of `http://ip:8080`. @@ -130,7 +130,7 @@ To reset everything: `docker compose down -v` (deletes the mongo-data volume). ## Coexistence with your LAMP stack -| Kino uses | LAMP uses | Conflict? | +| StreamHoard uses | LAMP uses | Conflict? | | ------------------------ | ----------------------- | --------- | | Port `KINO_HTTP_PORT` | Apache 80/443 | No — different ports | | MongoDB (internal only) | MySQL | No — different DBs, Mongo not exposed to host | @@ -144,16 +144,16 @@ same box. ## Radarr integration -If Radarr is running on the same Proxmox host (native or container), point Kino +If Radarr is running on the same Proxmox host (native or container), point StreamHoard at it via **Admin → Settings → Radarr**: - **URL**: `http://:7878` (use the host IP, not `localhost` — containers can't reach the host's `localhost` unless you add `host.docker.internal`) - **API key**: from Radarr → Settings → General → Security -**Important**: For Kino to actually play the imported movies, the path Radarr +**Important**: For StreamHoard to actually play the imported movies, the path Radarr reports (e.g. `/movies/Inception (2010)/Inception.mkv`) must be **readable from -inside the Kino backend container**. Mount Radarr's media path into Kino: +inside the StreamHoard backend container**. Mount Radarr's media path into StreamHoard: ```yaml # docker-compose.yml — extend the backend volumes @@ -163,7 +163,7 @@ backend: - /mnt/radarr-movies:/mnt/radarr-movies:ro # <— add this ``` -Radarr's file paths must be identical inside both Radarr and Kino containers +Radarr's file paths must be identical inside both Radarr and StreamHoard containers (easiest: match the paths exactly). --- @@ -172,14 +172,14 @@ Radarr's file paths must be identical inside both Radarr and Kino containers ```bash curl -s http://localhost:${KINO_HTTP_PORT:-8080}/api/ | jq -# {"app":"Kino","status":"ok"} +# {"app":"StreamHoard","status":"ok"} ``` --- ## Firewalling -Kino has no built-in rate limiting or IP allowlist. Since you confirmed +StreamHoard has no built-in rate limiting or IP allowlist. Since you confirmed **internal network only**, keep it that way: ```bash diff --git a/README.md b/README.md index 99a3ddd..d5b4632 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Kino — Personal Media Server (Netflix Clone) +# StreamHoard — Personal Media Server (Netflix Clone) A self-hosted Netflix-style streaming app for movies you legally own. Built with FastAPI + React + MongoDB. @@ -41,7 +41,7 @@ See **[DEPLOY.md](./DEPLOY.md)** for LAMP-coexistence, Apache reverse proxy, Rad *(Legal alternative to auto-downloading — auto-download of copyrighted material is not implemented.)* ## Default admin -- Email: `admin@kino.local` +- Email: `admin@streamhoard.local` - Password: `kino-admin-2026` Change `ADMIN_EMAIL` / `ADMIN_PASSWORD` in `backend/.env` before first start @@ -118,7 +118,7 @@ Movie media files should live on your Proxmox storage, not Git. # Login curl -X POST $URL/api/auth/login \ -H 'Content-Type: application/json' \ - -d '{"email":"admin@kino.local","password":"kino-admin-2026"}' + -d '{"email":"admin@streamhoard.local","password":"kino-admin-2026"}' # List movies curl $URL/api/movies @@ -130,7 +130,7 @@ curl $URL/api/stream/?auth= ## Why no auto-download? Auto-downloading copyrighted movies from the internet is illegal in most -jurisdictions (DMCA, EU copyright directive, etc.). Kino is built as a +jurisdictions (DMCA, EU copyright directive, etc.). StreamHoard is built as a **personal media server** for content you legally own or that is in the public domain (Internet Archive, Blender Open Movies, etc.). diff --git a/backend/server.py b/backend/server.py index b6bc276..83b8e70 100644 --- a/backend/server.py +++ b/backend/server.py @@ -57,7 +57,7 @@ for d in (VIDEOS_DIR, POSTERS_DIR, SUBS_DIR, HLS_DIR): CHUNK_SIZE = 1024 * 1024 -app = FastAPI(title="Kino") +app = FastAPI(title="StreamHoard") api = APIRouter(prefix="/api") bearer = HTTPBearer(auto_error=False) @@ -114,7 +114,7 @@ async def on_startup(): await db.profiles.create_index("user_id") await db.subtitles.create_index("movie_id") - admin_email = os.environ.get("ADMIN_EMAIL", "admin@kino.local") + admin_email = os.environ.get("ADMIN_EMAIL", "admin@streamhoard.local") if not await db.users.find_one({"email": admin_email}): admin_user = { "id": str(uuid.uuid4()), @@ -1482,7 +1482,7 @@ async def _auto_fetch_subtitle(content_type: str, content_id: str, tmdb_id: Opti # ============ HEALTH ============ @api.get("/") async def root(): - return {"app": "Kino", "status": "ok"} + return {"app": "StreamHoard", "status": "ok"} app.include_router(api) diff --git a/backend/tests/backend_test.py b/backend/tests/backend_test.py index e526054..7e98c7e 100644 --- a/backend/tests/backend_test.py +++ b/backend/tests/backend_test.py @@ -1,5 +1,5 @@ """ -Backend pytest suite for Kino Personal Media Server. +Backend pytest suite for StreamHoard Personal Media Server. Tests auth, movies, watchlist, progress, requests, and stream auth. """ import os @@ -8,8 +8,8 @@ import uuid import pytest import requests -BASE_URL = os.environ.get("REACT_APP_BACKEND_URL", "https://streamhoard.preview.emergentagent.com").rstrip("/") -ADMIN_EMAIL = "admin@kino.local" +BASE_URL = os.environ.get("REACT_APP_BACKEND_URL", "http://localhost:8001").rstrip("/") +ADMIN_EMAIL = "admin@streamhoard.local" ADMIN_PASSWORD = "kino-admin-2026" @@ -32,7 +32,7 @@ def admin_token(api): @pytest.fixture(scope="session") def member_token(api): - email = f"TEST_user_{uuid.uuid4().hex[:8]}@kino.local" + email = f"TEST_user_{uuid.uuid4().hex[:8]}@streamhoard.local" r = api.post(f"{BASE_URL}/api/auth/register", json={"email": email, "password": "pass1234", "name": "Test User"}) assert r.status_code == 200, f"register failed: {r.text}" data = r.json() @@ -73,7 +73,7 @@ class TestAuth: assert r.status_code == 401 def test_register_and_me(self, api): - email = f"TEST_reg_{uuid.uuid4().hex[:8]}@kino.local" + email = f"TEST_reg_{uuid.uuid4().hex[:8]}@streamhoard.local" r = api.post(f"{BASE_URL}/api/auth/register", json={"email": email, "password": "pass1234", "name": "Reg User"}) assert r.status_code == 200 d = r.json() @@ -86,7 +86,7 @@ class TestAuth: assert r2.json()["email"] == email.lower() def test_register_duplicate(self, api): - email = f"TEST_dup_{uuid.uuid4().hex[:8]}@kino.local" + email = f"TEST_dup_{uuid.uuid4().hex[:8]}@streamhoard.local" api.post(f"{BASE_URL}/api/auth/register", json={"email": email, "password": "pass1234", "name": "U"}) r = api.post(f"{BASE_URL}/api/auth/register", json={"email": email, "password": "pass1234", "name": "U"}) assert r.status_code == 400 diff --git a/backend/tests/test_phase2.py b/backend/tests/test_phase2.py index 9954492..f021548 100644 --- a/backend/tests/test_phase2.py +++ b/backend/tests/test_phase2.py @@ -9,7 +9,7 @@ import pytest import requests BASE_URL = os.environ.get("REACT_APP_BACKEND_URL", "").rstrip("/") -ADMIN_EMAIL = "admin@kino.local" +ADMIN_EMAIL = "admin@streamhoard.local" ADMIN_PASSWORD = "kino-admin-2026" @@ -36,7 +36,7 @@ def admin_headers(admin_token): @pytest.fixture(scope="module") def member(api): """Create a fresh member for profile-scoped tests.""" - email = f"TEST_p2_{uuid.uuid4().hex[:8]}@kino.local" + email = f"TEST_p2_{uuid.uuid4().hex[:8]}@streamhoard.local" r = api.post(f"{BASE_URL}/api/auth/register", json={"email": email, "password": "pass1234", "name": "P2 User"}) assert r.status_code == 200 @@ -101,7 +101,7 @@ class TestProfiles: def test_cannot_delete_last_profile(self, api): # fresh user with single profile - email = f"TEST_solo_{uuid.uuid4().hex[:6]}@kino.local" + email = f"TEST_solo_{uuid.uuid4().hex[:6]}@streamhoard.local" d = api.post(f"{BASE_URL}/api/auth/register", json={"email": email, "password": "pass1234", "name": "Solo"}).json() h = {"Authorization": f"Bearer {d['access_token']}"} diff --git a/backend/tests/test_phase4_queue.py b/backend/tests/test_phase4_queue.py index 3728e73..03f745c 100644 --- a/backend/tests/test_phase4_queue.py +++ b/backend/tests/test_phase4_queue.py @@ -17,7 +17,7 @@ import pytest import requests BASE_URL = os.environ["REACT_APP_BACKEND_URL"].rstrip("/") -ADMIN_EMAIL = "admin@kino.local" +ADMIN_EMAIL = "admin@streamhoard.local" ADMIN_PASSWORD = "kino-admin-2026" QTEST_VIDEO = "/tmp/qtest.mp4" @@ -40,7 +40,7 @@ def admin_token(api): @pytest.fixture(scope="session") def member_token(api): - email = f"TEST_q_{uuid.uuid4().hex[:8]}@kino.local" + email = f"TEST_q_{uuid.uuid4().hex[:8]}@streamhoard.local" r = api.post(f"{BASE_URL}/api/auth/register", json={"email": email, "password": "pass1234", "name": "QTest"}, headers={"Content-Type": "application/json"}) diff --git a/docker-compose.yml b/docker-compose.yml index 8013234..2faa32e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,9 +1,9 @@ -# Kino — Docker Compose deployment +# StreamHoard — Docker Compose deployment # # Runs alongside existing LAMP stacks without conflicts: # - Apache keeps port 80/443 -# - MySQL is untouched (Kino uses its own MongoDB in a container) -# - Kino exposes a single configurable port (default 8080) +# - MySQL is untouched (StreamHoard uses its own MongoDB in a container) +# - StreamHoard exposes a single configurable port (default 8080) # # Usage: # cp .env.compose.example .env @@ -14,19 +14,19 @@ services: mongo: image: mongo:4.4 - container_name: kino-mongo + container_name: streamhoard-mongo restart: unless-stopped volumes: - mongo-data:/data/db networks: - - kino - # No ports exposed to host — only reachable inside kino network + - streamhoard + # No ports exposed to host — only reachable inside streamhoard network backend: build: context: ./backend dockerfile: Dockerfile - container_name: kino-backend + container_name: streamhoard-backend restart: unless-stopped depends_on: - mongo @@ -37,7 +37,7 @@ services: JWT_SECRET: ${JWT_SECRET:?set JWT_SECRET in .env} JWT_ALG: HS256 JWT_EXPIRE_HOURS: ${JWT_EXPIRE_HOURS:-720} - ADMIN_EMAIL: ${ADMIN_EMAIL:-admin@kino.local} + ADMIN_EMAIL: ${ADMIN_EMAIL:-admin@streamhoard.local} ADMIN_PASSWORD: ${ADMIN_PASSWORD:?set ADMIN_PASSWORD in .env} ADMIN_NAME: ${ADMIN_NAME:-Admin} MEDIA_ROOT: /media @@ -45,25 +45,25 @@ services: # Host media directory (movies, HLS, subtitles) — point at your NAS/ZFS mount - ${MEDIA_ROOT_HOST:-./media}:/media networks: - - kino + - streamhoard # No direct host port — frontend container reverse-proxies /api → backend:8001 frontend: build: context: ./frontend dockerfile: Dockerfile - container_name: kino-frontend + container_name: streamhoard-frontend restart: unless-stopped depends_on: - backend ports: - # Only Kino's external port. Change KINO_HTTP_PORT to avoid clashes with Apache. + # Only StreamHoard's external port. Change KINO_HTTP_PORT to avoid clashes with Apache. - "${KINO_HTTP_PORT:-8080}:80" networks: - - kino + - streamhoard networks: - kino: + streamhoard: driver: bridge volumes: diff --git a/frontend/craco.config.js b/frontend/craco.config.js index ddbeea8..9f07edb 100644 --- a/frontend/craco.config.js +++ b/frontend/craco.config.js @@ -2,10 +2,6 @@ const path = require("path"); require("dotenv").config(); -// Check if we're in development/preview mode (not production build) -// Craco sets NODE_ENV=development for start, NODE_ENV=production for build -const isDevServer = process.env.NODE_ENV !== "production"; - // Environment variable overrides const config = { enableHealthCheck: process.env.ENABLE_HEALTH_CHECK === "true", @@ -81,20 +77,4 @@ webpackConfig.devServer = (devServerConfig) => { return devServerConfig; }; -// Wrap with visual edits (automatically adds babel plugin, dev server, and overlay in dev mode) -if (isDevServer) { - try { - const { withVisualEdits } = require("@emergentbase/visual-edits/craco"); - webpackConfig = withVisualEdits(webpackConfig); - } catch (err) { - if (err.code === 'MODULE_NOT_FOUND' && err.message.includes('@emergentbase/visual-edits/craco')) { - console.warn( - "[visual-edits] @emergentbase/visual-edits not installed — visual editing disabled." - ); - } else { - throw err; - } - } -} - module.exports = webpackConfig; diff --git a/frontend/package.json b/frontend/package.json index 8d3ba99..394db5b 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -76,7 +76,6 @@ "devDependencies": { "@babel/plugin-proposal-private-property-in-object": "^7.21.11", "@craco/craco": "^7.1.0", - "@emergentbase/visual-edits": "https://assets.emergent.sh/npm/emergentbase-visual-edits-1.0.8.tgz", "@eslint/js": "9.23.0", "autoprefixer": "^10.4.20", "eslint": "9.23.0", diff --git a/frontend/public/index.html b/frontend/public/index.html index 4741de5..6d9d86d 100644 --- a/frontend/public/index.html +++ b/frontend/public/index.html @@ -4,7 +4,7 @@ - + @@ -21,9 +21,8 @@ work correctly both with client-side routing and a non-root public URL. Learn how to configure a non-root public URL by running `npm run build`. --> - Emergent | Fullstack App + StreamHoard - @@ -38,76 +37,5 @@ To begin the development, run `npm start` or `yarn start`. To create a production bundle, use `npm run build` or `yarn build`. --> - diff --git a/frontend/src/components/Navbar.jsx b/frontend/src/components/Navbar.jsx index 9db34d6..aa0b2af 100644 --- a/frontend/src/components/Navbar.jsx +++ b/frontend/src/components/Navbar.jsx @@ -29,7 +29,7 @@ export const Navbar = () => {
- Kino + StreamHoard . {user && ( diff --git a/frontend/src/pages/AdminShowEpisodes.jsx b/frontend/src/pages/AdminShowEpisodes.jsx index 282fe24..6f229de 100644 --- a/frontend/src/pages/AdminShowEpisodes.jsx +++ b/frontend/src/pages/AdminShowEpisodes.jsx @@ -61,7 +61,7 @@ export default function AdminShowEpisodes() { const { data } = await api.post("/episodes/bulk-hide", { episode_ids: ids, hidden: false }); msg = `${data.modified} unhidden`; } else if (action === "delete") { - if (!window.confirm(`Delete ${ids.length} episode(s) permanently? Files on disk are kept, but Kino entries and HLS output are removed.`)) return; + if (!window.confirm(`Delete ${ids.length} episode(s) permanently? Files on disk are kept, but StreamHoard entries and HLS output are removed.`)) return; const { data } = await api.post("/episodes/bulk-delete", { episode_ids: ids }); msg = `${data.deleted} deleted`; } diff --git a/frontend/src/pages/Login.jsx b/frontend/src/pages/Login.jsx index f1ac093..e32fd5b 100644 --- a/frontend/src/pages/Login.jsx +++ b/frontend/src/pages/Login.jsx @@ -48,7 +48,7 @@ export default function Login() {
- Kino + StreamHoard .

Sign in

@@ -93,7 +93,7 @@ export default function Login() {
Demo Admin - admin@kino.local / kino-admin-2026 + admin@streamhoard.local / kino-admin-2026
diff --git a/frontend/src/pages/Register.jsx b/frontend/src/pages/Register.jsx index f623b06..965f266 100644 --- a/frontend/src/pages/Register.jsx +++ b/frontend/src/pages/Register.jsx @@ -20,7 +20,7 @@ export default function Register() { setSubmitting(true); try { await register(email, password, name); - toast.success("Welcome to Kino"); + toast.success("Welcome to StreamHoard"); nav("/browse", { replace: true }); } catch (err) { toast.error(err.response?.data?.detail || "Could not register"); @@ -49,7 +49,7 @@ export default function Register() {
- Kino + StreamHoard .

Create account

diff --git a/install.sh b/install.sh index 3616444..500d60f 100755 --- a/install.sh +++ b/install.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash # -# Kino — one-line installer +# StreamHoard — one-line installer # # Usage (interactive): # curl -fsSL https://raw.githubusercontent.com/YOU/kino-media-server/main/install.sh | sudo bash @@ -25,7 +25,7 @@ KINO_DIR="${KINO_DIR:-/opt/kino}" KINO_HTTP_PORT="${KINO_HTTP_PORT:-8080}" MEDIA_ROOT_HOST="${MEDIA_ROOT_HOST:-${KINO_DIR}/media}" DB_NAME="${DB_NAME:-kino}" -ADMIN_EMAIL="${ADMIN_EMAIL:-admin@kino.local}" +ADMIN_EMAIL="${ADMIN_EMAIL:-admin@streamhoard.local}" ADMIN_NAME="${ADMIN_NAME:-Admin}" ADMIN_PASSWORD="${ADMIN_PASSWORD:-}" JWT_SECRET="${JWT_SECRET:-}" @@ -172,7 +172,7 @@ fi # ---------- Done ---------- cat <