diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..97b81d6b --- /dev/null +++ b/.gitignore @@ -0,0 +1,46 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.* +.yarn/* +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/versions + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* + +# env files (can opt-in for committing if needed) +.env* + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts + +/src/generated/prisma +/dev.db +/dev.db-journal +/tmp/ \ No newline at end of file diff --git a/GEMINI.md b/GEMINI.md index 3ea20eba..76ddfc90 100644 --- a/GEMINI.md +++ b/GEMINI.md @@ -12,67 +12,8 @@ When executing an implementation plan, after a phase is completed, ask the user ## Technical specifications, tools and architecture -If asked to use icons or pictures in your app (for example hero images or background images) generate them with the Nano Banana extension - -## SQLAlchemy Database interactions coding guidelines - -When using SQLAlchemy with Python and Flask, all database models and queries must adhere to the modern **SQLAlchemy 2.0** style. The legacy query API from `Flask-SQLAlchemy` (`Model.query`) is forbidden. - -### 1. Model Definition - -Models must be defined using `sqlalchemy.orm.Mapped` and `sqlalchemy.orm.mapped_column` with type annotations. - -**Bad (Legacy Style):** -```python -class User(db.Model): - id = db.Column(db.Integer, primary_key=True) - email = db.Column(db.String(40), unique=True, nullable=False) -``` - -**Good (Modern SQLAlchemy 2.0 Style):** -```python -import sqlalchemy as sa -import sqlalchemy.orm as so - -class User(db.Model): - id: so.Mapped[int] = so.mapped_column(primary_key=True) - email: so.Mapped[str] = so.mapped_column(sa.String(40), unique=True) -``` - -### 2. Database Queries - -All queries must be constructed using the `sqlalchemy.select()` function. Do not use the `Model.query` object. - -**Bad (Legacy `Model.query`):** -```python -# Get by primary key -user = User.query.get(1) - -# Filter and get first -user = User.query.filter_by(email="test@example.com").first() - -# Get all -users = User.query.all() -``` - -**Good (Modern `select()` construct):** -```python -import sqlalchemy as sa - -# Get by primary key -user = db.session.get(User, 1) - -# Filter and get first -stmt = sa.select(User).where(User.email == "test@example.com") -user = db.session.scalars(stmt).first() - -# Get all -stmt = sa.select(User) -users = db.session.scalars(stmt).all() -``` - - - +If asked to use icons or pictures in your app (for example hero images or background images) generate them with the Nano Banana extension +For guide on interacting with Google Gemini API follow the instructions in: @gemini-styleguide.md diff --git a/IMPLEMENTATION_PLAN.md b/IMPLEMENTATION_PLAN.md new file mode 100644 index 00000000..d1df94d5 --- /dev/null +++ b/IMPLEMENTATION_PLAN.md @@ -0,0 +1,59 @@ +# Implementation Plan - Podcast Generator + +## Phase 0: Git Setup +- [x] Check if the current directory is an initialized git repository. +- [x] If it is, create and checkout a new feature branch named "podcast-generator". + +## Phase 1: Project Initialization +- [x] Initialize a new Next.js project with TypeScript and Tailwind CSS using `npx create-next-app@latest`. +- [x] Install Prisma and SQLite dependencies (`npm install prisma @prisma/client`, `npx prisma init`). +- [x] Install backend utilities: `rss-parser`, `fluent-ffmpeg`, `@google/generative-ai`. +- [x] Install UI icons (e.g., `lucide-react` or `heroicons`). +- [x] Configure `next.config.js` if necessary for external images or specific build settings. +- [x] Create a `.env` file for API keys (Gemini API Key) and database URL. + +## Phase 2: Database & Backend Basics +- [x] Define the Prisma schema (`Feed` and `Podcast` models) in `prisma/schema.prisma` as per the tech spec. +- [x] Run `npx prisma migrate dev --name init` to create the SQLite database tables. +- [x] Create a Prisma client instance singleton (`lib/prisma.ts`) to avoid connection exhaustion in dev. +- [x] Implement `POST /api/feeds` API route: Validate URL, fetch title with `rss-parser`, save to DB. +- [x] Implement `GET /api/feeds` API route: Retrieve all feeds from DB. +- [x] Implement `DELETE /api/feeds` API route: Remove a feed by ID. + +## Phase 3: Core Logic (AI & Audio) +- [x] Create a utility function `lib/gemini.ts` to handle Gemini API interactions (Summarization). +- [x] Create a utility function `lib/audio.ts` to handle Text-to-Speech (using Google Cloud TTS or Gemini Multimodal if available/configured). +- [x] Install FFmpeg locally or ensure it's available in the environment (for `fluent-ffmpeg` to work). +- [x] Create `lib/ffmpeg.ts` to handle audio stitching (Intro + Segments + Outro). +- [x] Implement the `POST /api/generate` logic: + - [x] Fetch feeds from DB. + - [x] Parse latest articles (limit 3-5). + - [x] Loop: Summarize articles using Gemini. + - [x] Loop: Generate audio segments from summaries. + - [x] Stitch segments into a single MP3. + - [x] Save MP3 to `public/podcasts/`. + - [x] Save metadata to `Podcast` table in DB. + +## Phase 4: Frontend Implementation +- [x] Generate the Hero Image using Nano Banana (prompt: "robot agent reading the news in a TV studio"). +- [x] Create `components/Hero.tsx` to display the hero image and title. +- [x] Create `components/FeedManager.tsx`: + - [x] Input field for RSS URL. + - [x] List of added feeds with delete button. + - [x] Connect to `GET`, `POST`, `DELETE /api/feeds`. +- [x] Create `components/PodcastGenerator.tsx`: + - [x] "Generate Podcast" button. + - [x] Loading state/spinner handling (long-running process). +- [x] Create `components/PodcastList.tsx`: + - [x] Fetch and display list of generated podcasts (`GET /api/podcasts`). + - [x] Embed HTML5 `