Skip to content
Closed
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
41 changes: 41 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# 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
5 changes: 4 additions & 1 deletion GEMINI.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@ 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
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 Gemini API follow the instructions in:
@gemini-styleguide.md

## SQLAlchemy Database interactions coding guidelines

Expand Down
48 changes: 48 additions & 0 deletions IMPLEMENTATION_PLAN.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# 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 & Configuration
- [x] Initialize a new Next.js project using `create-next-app` with TypeScript, Tailwind CSS, and App Router.
- [x] Install necessary dependencies: `google-genai` (using `@google/generative-ai` for Node), `@google-cloud/text-to-speech`, `rss-parser`, `fluent-ffmpeg`, `uuid`.
- [x] Install dev dependencies for types: `@types/fluent-ffmpeg`, `@types/uuid`.
- [x] Configure `tailwind.config.ts` (Using Tailwind v4 default CSS configuration).
- [x] Create the directory structure as defined in the Tech Spec (`/lib`, `/public/podcasts`, `/app/api/...`).
- [x] Set up environment variables (e.g., `GOOGLE_API_KEY`, `GOOGLE_APPLICATION_CREDENTIALS`).

## Phase 2: Backend Core Libraries (Data & Logic)
- [x] Implement `/lib/storage.ts`: Create functions to read/write `feeds.json` and `podcasts.json` (mock DB). Define TypeScript interfaces `Feed` and `Podcast`.
- [x] Implement `/lib/rss.ts`: Create function `fetchLatestArticles(feedUrl)` using `rss-parser`.
- [x] Implement `/lib/gemini.ts`: Initialize Google Gen AI client and create `summarizeArticle(text)` function using `gemini-2.5-flash`.
- [x] Implement `/lib/tts.ts`: Initialize Google Cloud TTS client and create `generateAudioSegment(text)` function.
- [x] Implement `/lib/audio.ts`: Logic to merge audio segments using `fluent-ffmpeg`.

## Phase 3: API Routes Implementation
- [x] Implement `GET /api/feeds`: Retrieve all feeds from storage.
- [x] Implement `POST /api/feeds`: Validate URL, fetch title, add to storage.
- [x] Implement `DELETE /api/feeds`: Remove feed by ID.
- [x] Implement `GET /api/podcasts`: Retrieve podcast history.
- [x] Implement `POST /api/generate`: Orchestrate the full pipeline (Fetch -> Summarize -> Script -> Synthesize -> Merge -> Save).

## Phase 4: Frontend Components & UI
- [x] Generate the "Robot News Anchor" hero image using Nano Banana and save to `/public`.
- [x] Create `FeedList` component: Display feeds, input for new feed, delete button. Connect to API.
- [x] Create `PodcastPlayer` component: List previous podcasts, play audio, download button. Connect to API.
- [x] Implement `HomePage` (`/app/page.tsx`): Assemble the Hero, FeedList, Generate Button, and PodcastPlayer.
- [x] Add loading states and error handling for the "Generate Podcast" action.

## Phase 5: Testing & Refinement
- [x] Verify RSS feed addition with valid/invalid URLs.
- [x] Test Podcast Generation flow (end-to-end) and check generated MP3 quality/content.
- [x] Ensure audio playback works correctly in the browser.
- [x] Polish UI with Tailwind CSS (responsiveness, spacing, typography).

## Phase 6: Completion & Version Control
- [x] Verify application functionality.
- [x] Create a `README.md` file explaining the application functions, how to interact with them, the architecture, file breakdown and how to run and test it locally.
- [x] Add all changes to the repository (`git add .`).
- [x] Commit the changes (`git commit -m "Complete implementation of Podcast Generator"`).
- [x] Push the feature branch to the remote repository, creating a branch with the same name in the remote repository, using the Gemini CLI github MCP server.
- [x] Open a pull request for the feature branch using the Gemini CLI github MCP server, leave it open for review, don't merge it.
166 changes: 72 additions & 94 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,94 +1,72 @@
# Spec-Driven Development w Gemini CLI

This repo has some basic assets to experiment **Spec-Driven Development** using the Gemini CLI. You will act as a developer going from a raw Functional Specification to a deployed Pull Request in a single session.

## Assets

* `.gemini/commands/`: Contains configuration files for custom commands (`techspec`, `plan`, `build`).
* `GEMINI.md`: Contains project rules and guidelines.
* `.github/workflows`: Contains CI workflow.
* **No application code**.

## Requirements

The `GEMINI.md` configuration and custom commands require the following extensions:
* **Google Workspace**
* **Nano Banana**
* **GitHub**

---

## Step 1: The Architect Phase (/techspec)

**Goal:** Transform a Functional Spec (Google Doc) into a Technical Spec (Google Doc).

1. **Command:**
```
/techspec "Name of your functional specs doc" "Your desired technology stack and requirements"
```

2. **What Happens:**
* The agent searches your Drive for the doc.
* It reads the requirements.
* It generates a **Technical Specification** including Data Models, API Routes, and Architecture based on your inputs.
* **Output:** It creates a *new* Google Doc titled "Technical Specification - Application name" and gives you the link.

---

## Step 2: The Planning Phase (/plan)

**Goal:** Break the Technical Spec down into an atomic Implementation Plan.

1. **Command:**
```
/plan "Name of your Tech spec doc"
```
*(Use the exact name of the doc generated in Step 1)*

2. **What Happens:**
* The agent reads the Tech Spec.
* It creates a local file `IMPLEMENTATION_PLAN.md`.
* It breaks the project into phases (e.g., Setup, Backend, Frontend, Polish).
* It defines the Git strategy.

---

## Step 3: The Build Phase (/build)

**Goal:** Execute the plan and write the code.

1. **Command:**
```
/build IMPLEMENTATION_PLAN.md "Name of your Tech spec doc"
```

2. **What Happens (Iterative):**
* **Execution:** The agent iterates through the plan, initializing the project structure and writing the application code.
* **Visuals:** It generates necessary visual assets (images, icons) as defined in the spec.
* **Progress:** It updates `IMPLEMENTATION_PLAN.md` as tasks are completed.

---

## Step 4: Final Delivery

**Goal:** Push the code and open a Pull Request.

1. **Action:**
The `/build` command's final phase usually covers this, or you can manually instruct the agent to finalize the project.

2. **What Happens:**
* The agent runs final checks (linting/formatting).
* It creates a `README.md` for the new application.
* It commits all changes.
* It pushes the feature branch to GitHub.
* It uses the GitHub extension to **Open a Pull Request**.

---

## Summary of Commands

| Step | Command | Input | Output |
| :--- | :--- | :--- | :--- |
| **1. Spec** | `/techspec` | Functional Doc (Drive) | Tech Spec (Drive) |
| **2. Plan** | `/plan` | Tech Spec (Drive) | `IMPLEMENTATION_PLAN.md` |
| **3. Build** | `/build` | Plan + Tech Spec | Code, Assets, App |
# AI News Anchor - Podcast Generator

A Next.js application that automatically generates personalized news podcasts from your favorite RSS feeds using Google Gemini for summarization and Google Cloud Text-to-Speech for narration.

## Features

- **RSS Feed Management**: Add and manage your favorite news sources (RSS/Atom feeds).
- **AI Summarization**: Uses Google's **Gemini 2.5 Flash** model to read articles and generate concise, conversational summaries suitable for a podcast format.
- **Neural Text-to-Speech**: Converts the generated script into high-quality, natural-sounding audio using Google Cloud TTS.
- **Audio Compilation**: Merges intro, story segments, and outro into a single downloadable MP3 file.
- **Podcast History**: Listen to or download previous daily digests.
- **Modern UI**: Built with Next.js App Router and Tailwind CSS, featuring a responsive dark mode design.

## Architecture

- **Frontend**: Next.js (React), Tailwind CSS
- **Backend**: Next.js API Routes (Serverless functions)
- **AI/ML**:
- `@google/generative-ai` (Gemini) for text processing.
- `@google-cloud/text-to-speech` for audio synthesis.
- **Data**: Local JSON storage (simple MVP).
- **Audio Processing**: `fluent-ffmpeg` for stitching audio files.

## Prerequisites

1. **Node.js** (v18+) and **npm**.
2. **Google Cloud Project** with:
- Generative AI API enabled (Gemini).
- Text-to-Speech API enabled.
3. **FFmpeg** installed on your system.
- macOS: `brew install ffmpeg`
- Linux: `sudo apt install ffmpeg`
- Windows: Download and add to PATH.

## Setup & Running Locally

1. **Clone the repository**:
```bash
git clone <repository-url>
cd podcast-generator
```

2. **Install dependencies**:
```bash
npm install
```

3. **Environment Configuration**:
Create a `.env.local` file in the root directory:
```env
GOOGLE_API_KEY=your_gemini_api_key
# If using a Service Account for Google Cloud TTS (Recommended)
GOOGLE_APPLICATION_CREDENTIALS="path/to/your/service-account-key.json"
```

4. **Run the development server**:
```bash
npm run dev
```

5. **Open the app**:
Visit [http://localhost:3000](http://localhost:3000).

## Usage

1. **Add Feeds**: Paste an RSS URL (e.g., `http://feeds.bbci.co.uk/news/rss.xml`) into the "Add Feed" input.
2. **Generate**: Click "Generate New Episode". The AI will fetch the latest articles, summarize them, and create an MP3. *Note: This process may take 1-2 minutes.*
3. **Listen**: The new episode will appear in the player on the right. Click play or download.

## License

MIT
56 changes: 56 additions & 0 deletions app/api/feeds/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { NextResponse } from 'next/server';
import { v4 as uuidv4 } from 'uuid';
import { getFeeds, saveFeeds, Feed } from '@/lib/storage';
import { fetchFeedTitle } from '@/lib/rss';

export async function GET() {
const feeds = getFeeds();
return NextResponse.json(feeds);
}

export async function POST(request: Request) {
try {
const body = await request.json();
const { url } = body;

if (!url) {
return NextResponse.json({ error: 'URL is required' }, { status: 400 });
}

const title = await fetchFeedTitle(url);
const newFeed: Feed = {
id: uuidv4(),
url,
title,
addedAt: new Date().toISOString(),
};

const feeds = getFeeds();
feeds.push(newFeed);
saveFeeds(feeds);

return NextResponse.json(newFeed, { status: 201 });
} catch (error) {
console.error('Error adding feed:', error);
return NextResponse.json({ error: 'Failed to add feed' }, { status: 500 });
}
}

export async function DELETE(request: Request) {
const { searchParams } = new URL(request.url);
const id = searchParams.get('id');

if (!id) {
return NextResponse.json({ error: 'ID is required' }, { status: 400 });
}

const feeds = getFeeds();
const filteredFeeds = feeds.filter(feed => feed.id !== id);

if (feeds.length === filteredFeeds.length) {
return NextResponse.json({ error: 'Feed not found' }, { status: 404 });
}

saveFeeds(filteredFeeds);
return NextResponse.json({ message: 'Feed deleted successfully' });
}
Loading