Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -50,5 +50,17 @@ google-cloud-key.json
/render_output/
*.aep

# local SQLite database (desktop build)
*.db
*.db-journal
*.db-wal
*.db-shm

# Tauri
/src-tauri/target/
/src-tauri/binaries/
# generated by scripts/prepare-sidecar.mjs (staged standalone build + seed db)
/src-tauri/resources/

# local Claude Code agent config
/.claude/
60 changes: 45 additions & 15 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@

A video rendering and upload management system for After Effects projects. Made by **rumitx** (author/creator).

> **Desktop migration (in progress):** Temlet is being adapted into a **Tauri v2
> desktop app** (Windows/macOS), not just a web app. The React UI runs in a native
> webview; the existing Next.js backend runs as an **embedded local sidecar server**;
> data is a **local SQLite file** (migrated off PostgreSQL). When changing the
> backend, DB, build, or env handling, keep the desktop path working — see
> **`DESKTOP.md`** (how it works) and **`DESKTOP_ROADMAP.md`** (what's left to do).

> **Brand note:** The product/app brand is **Temlet** (renamed from "RumitX Studio").
> "RumitX" remains the author/creator identity — keep "made by rumitx" attribution as-is.
> The **YouTube content channels** ("RumitX Studio", "RumitX Shorts", "RumitX Nature",
Expand All @@ -11,25 +18,32 @@ A video rendering and upload management system for After Effects projects. Made

## Stack

- **Next.js 15** (App Router) + **React 19** + **TypeScript** (strict)
- **Next.js 16** (App Router) + **React 19** + **TypeScript** (strict)
- **Tailwind CSS v4** (PostCSS), **Framer Motion**
- **Prisma 6** + **PostgreSQL**
- **Prisma 7** + **SQLite** (local file, via `@prisma/adapter-better-sqlite3`) —
migrated off PostgreSQL for self-contained desktop use
- **Tauri v2** (Rust shell in `src-tauri/`) for the desktop build
- **Puppeteer** (crawlers), **fluent-ffmpeg** / `ffmpeg-static`, **sharp**
- **googleapis** (YouTube), TikTok upload, **OpenAI** / Grok image generation
- Dev server runs on **port 3001**
- Dev server runs on **port 3001**; the packaged desktop server runs on `127.0.0.1:38211`

## Commands

```bash
npm run dev # next dev --port 3001
npm run build # next build
npm run start # next start
npm run lint # next lint
npm run monitor # node scripts/monitor.js (render monitor)
npm run dev # next dev --port 3001
npm run build # next build (output: "standalone" for the sidecar)
npm run start # next start
npm run lint # next lint
npm run monitor # node scripts/monitor.js (render monitor, web/dev)
npm run tauri:dev # desktop window wrapping next dev
npm run tauri:build # build the desktop app (runs build + prepare:sidecar)
npm run prepare:sidecar # stage standalone build + seed DB into src-tauri/resources
```

Prisma: `npx prisma generate`, `npx prisma migrate dev`, `npx prisma studio`.
Docker: see `README-DOCKER.md` (`docker-compose.dev.yml` / `.prod.yml`).
Prisma (SQLite): export `DATABASE_URL="file:./prisma/temlet.db"`, then
`npx prisma generate`, `npx prisma migrate dev`, `npx prisma studio`.
Prisma 7 does not auto-load `.env` — set `DATABASE_URL` in the shell.
Docker: see `README-DOCKER.md` (web deployment; uses its own DB config).

## Layout

Expand All @@ -55,10 +69,14 @@ app/
├── layout.tsx # Root layout + metadata
├── assets/ crawlers/ ae_render_jobs/ render_* / callback/ tiktok-callback/
lib/
├── prisma.ts # Prisma client singleton
├── prisma.ts # Prisma client singleton (better-sqlite3 adapter)
└── config.ts # WORKING_DIRECTORY + channel/topic path helpers
prisma/schema.prisma # Models: RenderFormat, RenderItem, Template, OutputFolder, CrawlerJob
scripts/monitor.js # Render monitor entrypoint
prisma/schema.prisma # provider = sqlite. Models: RenderFormat, RenderItem, Template, OutputFolder, CrawlerJob
instrumentation.ts # In-process render monitor (when TEMLET_RUN_MONITOR=1; packaged app)
scripts/monitor.js # Render monitor entrypoint (web/dev)
scripts/prepare-sidecar.mjs # Stage standalone build + seed DB for the desktop bundle
loading/index.html # Desktop startup splash (shown until the sidecar is ready)
src-tauri/ # Tauri v2 Rust shell (lib.rs spawns/seeds/manages the sidecar)
postman/ # Temlet.postman_collection.json (API collection)
```

Expand All @@ -69,13 +87,25 @@ Path alias: `@/*` → project root (see `tsconfig.json`).
- **Channels → folders**: `lib/config.ts` builds asset paths from `WORKING_DIRECTORY/<channel>/<topic>/<category>` using lowercased names. Channel/topic strings are functional, not just labels.
- **Render pipeline**: render items (`RenderItem`) created from templates → rendered via nexrender → metadata generated → uploaded to YouTube/TikTok. Scheduling distributes uploads across days/time slots (see `README.md`).
- **AI image generation**: `app/api/assets/generate-image` supports OpenAI DALL·E 3, Grok-2 Image, and local ComfyUI.
- **Desktop sidecar**: `src-tauri/src/lib.rs` — in **dev** the window wraps `next dev`
(no sidecar); in **release** it boots the bundled standalone server, seeds
`temlet.db` into the app-data dir on first run, injects secrets from
`<app-config>/temlet.env`, waits for the port, then navigates the window to it.
The child process is killed on app exit.

## Conventions

- Follow repo TypeScript style: explicit types on exported/public APIs, avoid `any`, immutable updates, no `console.log` in production code.
- Validate input at API boundaries; never trust external data (crawled content, API responses).
- Secrets via env only (see `env.example`): `DATABASE_URL`, `OPENAI_API_KEY`, `GROK_API_KEY`, `GOOGLE_CLIENT_ID/SECRET`, `NEXTAUTH_SECRET`. Never hardcode.
- Secrets via env only — never hardcode. Web/dev: `env.example` (`DATABASE_URL` is a
SQLite `file:` URL, `OPENAI_API_KEY`, `GROK_API_KEY`, `GOOGLE_CLIENT_ID/SECRET`).
Desktop: `temlet.env.example` (loaded from the OS app-config dir by the shell).
- When migrating the schema, remember the packaged app seeds the DB only on first
run — schema changes shipped in a new app version need a migration-on-update path
(see `DESKTOP_ROADMAP.md`, Phase B).

## Setup docs

`README.md`, `README-DOCKER.md`, `DATABASE_SETUP.md`, `CRAWLERS_README.md`, `TIKTOK_SETUP.md`, `WORKING_DIRECTORY_SETUP.md`.
`README.md`, `DESKTOP.md` (desktop build), `DESKTOP_ROADMAP.md` (desktop TODO),
`README-DOCKER.md`, `DATABASE_SETUP.md`, `CRAWLERS_README.md`, `TIKTOK_SETUP.md`,
`WORKING_DIRECTORY_SETUP.md`.
39 changes: 30 additions & 9 deletions DATABASE_SETUP.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,19 +29,40 @@ The `CrawlerJob` model is already available in your Prisma client and the databa
- **Real Data**: All data persists across app restarts
- **Live Updates**: Job status and progress updates in real-time

## 🗄️ **Database: local SQLite**

Temlet uses a **local SQLite file** (via Prisma's `better-sqlite3` driver adapter),
so there is no separate database server to install or run. In the packaged desktop
app the Tauri shell points the database at a writable app-data path automatically.

For local development, set a project-relative file URL:

```env
DATABASE_URL="file:./prisma/temlet.db"
```

Then create/sync the schema and generate the client:

```bash
# create or update the local SQLite DB from prisma/schema.prisma
DATABASE_URL="file:./prisma/temlet.db" npx prisma migrate dev

# (re)generate the Prisma client
npx prisma generate
```

> Note: Prisma 7 no longer auto-loads `.env`. Populate the shell env (as shown
> above) or use `dotenv -e .env -- prisma ...` when running Prisma CLI commands.

## 🔧 **If You Encounter Issues**

If you get any database connection errors:
If you get any database errors:

1. **Check your `.env` file**:
```env
DATABASE_URL="postgresql://username:password@localhost:5432/render_manager"
```
1. **Check your `.env` file** has a SQLite `file:` URL (see above).

2. **Verify PostgreSQL is running**:
2. **Inspect the database** with Prisma Studio:
```bash
# Check if PostgreSQL is accessible
psql -h localhost -U your_username -d render_manager
DATABASE_URL="file:./prisma/temlet.db" npx prisma studio
```

3. **Restart your development server**:
Expand All @@ -58,4 +79,4 @@ Your crawler system is now fully functional with:
- ✅ Real-time statistics
- ✅ Job lifecycle management

Go ahead and start creating crawler jobs - everything will be saved to your PostgreSQL database!
Go ahead and start creating crawler jobs - everything will be saved to your local SQLite database!
101 changes: 101 additions & 0 deletions DESKTOP.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
# Temlet Desktop (Tauri)

Temlet ships as a desktop app (Windows / macOS) using [Tauri v2](https://tauri.app).
The React UI runs in the native webview; the existing Next.js backend (all API
routes, render/crawler services, Puppeteer, ffmpeg, Prisma) runs as an embedded
local server. Data lives in a local **SQLite** file, so no separate database
server is required.

## Architecture

- **Dev (`npm run tauri:dev`)** — opens a native window pointed at the live
`next dev` server (`http://localhost:3001`). Fast iteration; same as the web app.
- **Release (`npm run tauri:build`)** — the Rust shell (`src-tauri/`) launches a
bundled Next.js **standalone** server on `127.0.0.1:38211`, waits for the port,
then navigates the window from a loading splash to the server.

Key pieces:

| Path | Role |
|------|------|
| `src-tauri/src/lib.rs` | Rust shell: spawns the server (release), seeds the DB, injects env, kills the process on exit |
| `next.config.ts` | `output: "standalone"` + tracing root/excludes/includes |
| `scripts/prepare-sidecar.mjs` | Stages the standalone build + seed DB into `src-tauri/resources/` |
| `instrumentation.ts` | Runs the render monitor in-process when `TEMLET_RUN_MONITOR=1` |
| `loading/index.html` | Splash shown until the embedded server is ready |

## Prerequisites

- Node.js ≥ 20, npm
- Rust toolchain (`rustup`, `cargo`) — required by Tauri
- Platform build deps per the [Tauri prerequisites](https://tauri.app/start/prerequisites/)

## Database (SQLite)

Set a local file URL and create/sync the schema:

```bash
export DATABASE_URL="file:./prisma/temlet.db"
npx prisma migrate dev # create/update the local DB
npx prisma generate # regenerate the client
```

> Prisma 7 does not auto-load `.env`; export `DATABASE_URL` in your shell (or use
> `dotenv -e .env -- prisma ...`). In the packaged app the shell points the DB at
> a writable app-data path automatically and seeds it from
> `src-tauri/resources/seed/temlet.db` on first run.

## Run in development

```bash
npm run tauri:dev
```

Opens the desktop window wrapping `next dev`. The render monitor still runs the
classic way (`npm run monitor`) in dev.

## Build a desktop app

```bash
npm run tauri:build
```

This runs `next build` → `fetch:node` → `prepare:sidecar` (staging) → `tauri build`,
producing a bundle for the current platform under
`src-tauri/target/release/bundle/`.

> The app **bundles its own Node runtime** (`scripts/fetch-node-runtime.mjs` stages
> the official binary into `src-tauri/resources/runtime/`), so it runs on a machine
> with no Node installed and regardless of how it's launched. `resolve_node()`
> prefers the bundled runtime, then `TEMLET_NODE_PATH`, then PATH. For cross-builds,
> set `TARGET_PLATFORM` / `TARGET_ARCH` / `TARGET_NODE_VERSION` before `fetch:node`
> (and rebuild native modules for that target).

## Runtime secrets

Open **Settings** (gear icon in the dashboard header, desktop only) to enter API
keys, Nexrender/YouTube/TikTok credentials, and the working directory. Values are
stored in the **OS keychain** (macOS Keychain / Windows Credential Manager) via
`src-tauri/src/secrets.rs`, not in a plaintext file. "Save & Restart" applies them
(the embedded server reads config from the keychain at startup). `CRON_SECRET` is
generated and persisted per install.

Legacy fallback: the shell also reads `temlet.env` from the OS app-config dir
(keychain values take precedence). Copy `temlet.env.example` there if preferred:

- macOS: `~/Library/Application Support/com.rumitx.temlet/temlet.env`
- Windows: `%APPDATA%\com.rumitx.temlet\temlet.env`

## Bundled runtimes

The build stages everything the app needs into `src-tauri/resources/` so it runs
on a clean machine (~600MB total): the Node runtime (`fetch:node`), the Next.js
server + seed DB (`prepare:sidecar`), and **Chromium for the crawler**
(`fetch:chromium` → `PUPPETEER_EXECUTABLE_PATH`). **ffmpeg** ships via
`ffmpeg-static` in the server bundle (`FFMPEG_PATH`). Cross-build with the
`TARGET_*` env vars before each `fetch:*`.

## Not yet done (distributable milestone)

- Code signing, notarization, installers, and auto-update.
- OAuth redirect handling for a signed app (custom scheme / deep link).
Loading