Skip to content
Open
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
8 changes: 8 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Flask Configuration
FLASK_APP=app.py
FLASK_ENV=development
SECRET_KEY=your-secret-key-here

# ForgeTube API Keys
GEMINI_API_KEY=your-gemini-api-key-here
SERP_API_KEY=your-serp-api-key-here
186 changes: 186 additions & 0 deletions app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
from flask import Flask, render_template, request, redirect, url_for, jsonify, session, send_from_directory
import os
import json
import uuid
from werkzeug.utils import secure_filename
import time
import threading
from datetime import datetime


app = Flask(__name__)
app.secret_key = os.urandom(24)
app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024 # 16MB max upload size
app.config['UPLOAD_FOLDER'] = 'uploads'
app.config['RESULTS_FOLDER'] = 'results'

# Ensure upload and results directories exist
os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True)
os.makedirs(app.config['RESULTS_FOLDER'], exist_ok=True)

# Task tracking storage
tasks = {}

# Custom Jinja2 filters
@app.template_filter('timestamp_to_datetime')
def timestamp_to_datetime(timestamp):
"""Convert Unix timestamp to formatted datetime string"""
return datetime.fromtimestamp(timestamp).strftime('%Y-%m-%d %H:%M:%S')

# Helper functions
def create_or_check_folder(folder_path):
"""
Creates a folder if it doesn't exist.
If folder exists, checks for files and returns error message if any are found.

Args:
folder_path (str): Path to the folder

Returns:
tuple: (success, message)
"""
if not os.path.exists(folder_path):
os.makedirs(folder_path)
return True, f"Created Folder: {folder_path}"
else:
if any(os.listdir(folder_path)):
return False, f"Folder '{folder_path}' already exists and contains files. Please remove them or use a different folder."
return True, f"Folder '{folder_path}' exists but is empty."

def generate_video_task(task_id, topic, duration, key_points, api_keys):
"""
Background task to generate video
"""
task = tasks[task_id]
task['status'] = 'Generating script...'
time.sleep(2) # Simulate script generation

# TODO: Replace with actual script generation
# generator = VideoScriptGenerator(api_key=api_keys['gemini'], serp_api_key=api_keys['serp'])
# script = generator.generate_script(topic, duration, key_points)

script = {"topic": topic, "audio_script": [{"text": "This is a test script"}]}
task['script'] = script
task['status'] = 'Script generated'

task['status'] = 'Generating images...'
time.sleep(3) # Simulate image generation

task['status'] = 'Generating audio...'
time.sleep(2) # Simulate audio generation

task['status'] = 'Assembling video...'
time.sleep(3) # Simulate video assembly

task['status'] = 'Completed'
task['result_url'] = f"/results/{task_id}.mp4"

# Routes
@app.route('/')
def index():
"""Home page"""
return render_template('index.html')

@app.route('/create', methods=['GET', 'POST'])
def create():
"""Create new video page"""
if request.method == 'POST':
# Get form data
topic = request.form.get('topic', '')
duration = int(request.form.get('duration', 60))
key_points_text = request.form.get('key_points', '')
key_points = [point.strip() for point in key_points_text.split(',') if point.strip()]

# Get API keys
gemini_api = request.form.get('gemini_api', '')
serp_api = request.form.get('serp_api', '')

# Validate inputs
if not topic:
return render_template('create.html', error="Topic is required")

if not gemini_api or not serp_api:
return render_template('create.html',
error="API keys are required. Get your Gemini API key at https://aistudio.google.com/apikey and Serp API key at https://serpapi.com")

# Create task
task_id = str(uuid.uuid4())
task = {
'id': task_id,
'topic': topic,
'duration': duration,
'key_points': key_points,
'status': 'Queued',
'created_at': time.time(),
'api_keys': {
'gemini': gemini_api,
'serp': serp_api
}
}
tasks[task_id] = task

# Start task in background
threading.Thread(
target=generate_video_task,
args=(task_id, topic, duration, key_points, task['api_keys'])
).start()

# Redirect to task progress page
return redirect(url_for('task_progress', task_id=task_id))

return render_template('create.html')

@app.route('/task/<task_id>')
def task_progress(task_id):
"""Task progress page"""
task = tasks.get(task_id)
if not task:
return render_template('error.html', message="Task not found"), 404

return render_template('progress.html', task=task)

@app.route('/api/task/<task_id>')
def task_status(task_id):
"""API endpoint to get task status"""
task = tasks.get(task_id)
if not task:
return jsonify({"error": "Task not found"}), 404

# Return task data without API keys for security
safe_task = {**task}
if 'api_keys' in safe_task:
del safe_task['api_keys']

return jsonify(safe_task)

@app.route('/refine/<task_id>', methods=['GET', 'POST'])
def refine_script(task_id):
"""Refine script page"""
task = tasks.get(task_id)
if not task:
return render_template('error.html', message="Task not found"), 404

if request.method == 'POST':
feedback = request.form.get('feedback', '')
if feedback:
# TODO: Implement script refinement with API
# generator = VideoScriptGenerator(api_key=task['api_keys']['gemini'],
# serp_api_key=task['api_keys']['serp'])
# refined_script = generator.refine_script(task['script'], feedback)
# task['script'] = refined_script

# For now, just acknowledge the feedback
task['feedback'] = feedback
task['status'] = 'Script refined, ready to generate'

return redirect(url_for('task_progress', task_id=task_id))

return render_template('refine.html', task=task)

@app.route('/results/<path:filename>')
def get_result(filename):
"""Serve result files"""
return send_from_directory(app.config['RESULTS_FOLDER'], filename)

if __name__ == '__main__':
app.run(debug=True)
50 changes: 50 additions & 0 deletions config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import os

class Config:
"""Base configuration"""
SECRET_KEY = os.environ.get('SECRET_KEY') or os.urandom(24)
MAX_CONTENT_LENGTH = 16 * 1024 * 1024 # 16MB max upload size
UPLOAD_FOLDER = 'uploads'
RESULTS_FOLDER = 'results'

# ForgeTube resources paths
SCRIPTS_PATH = "resources/scripts/"
IMAGES_PATH = "resources/images/"
AUDIO_PATH = "resources/audio/"
FONT_PATH = "resources/font/font.ttf"

# Default video settings
DEFAULT_DURATION = 60 # in seconds
MAX_DURATION = 300 # 5 minutes max

# UI Settings
ACCENT_COLOR = "#6366F1" # Indigo
DARK_BG = "#121212"
DARKER_BG = "#0A0A0A"
TEXT_COLOR = "#E5E7EB"

class DevelopmentConfig(Config):
"""Development configuration"""
DEBUG = True
TESTING = False

class TestingConfig(Config):
"""Testing configuration"""
DEBUG = False
TESTING = True

class ProductionConfig(Config):
"""Production configuration"""
DEBUG = False
TESTING = False

config_by_name = {
'development': DevelopmentConfig,
'testing': TestingConfig,
'production': ProductionConfig
}

def get_config():
"""Return the appropriate configuration object based on environment variable"""
env = os.environ.get('FLASK_ENV', 'development')
return config_by_name[env]
95 changes: 95 additions & 0 deletions flask_frontend_README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# ForgeTube Web Frontend

A modern, dark-themed web interface for ForgeTube, an AI-powered video generation system.

## Overview

This Flask application provides an intuitive web interface for interacting with ForgeTube's automated video generation capabilities. It allows users to:

1. Input topics, durations, and key points for video generation
2. Review and refine AI-generated scripts
3. Track the progress of video generation
4. Preview and download the final videos

## Features

- **Modern, Dark UI**: Sleek, minimalistic interface with a dark color scheme
- **Responsive Design**: Works well on desktop and mobile devices
- **Real-time Progress Tracking**: Live updates on video generation status
- **Script Refinement**: Interactive script review and feedback system
- **Secure API Key Management**: User-provided API keys used securely for content generation

## Installation

1. Clone the repository:

```bash
git clone https://github.com/MLSAKIIT/ForgeTube.git
cd ForgeTube
```

2. Create and activate a virtual environment:

```bash
python -m venv venv
# On Windows
venv\Scripts\activate
# On macOS/Linux
source venv/bin/activate
```

3. Install dependencies:

```bash
pip install -r requirements.txt
```

4. Create a `.env` file based on `.env.example`:
```bash
cp .env.example .env
```
Then edit the `.env` file with your API keys.

## Usage

1. Start the Flask development server:

```bash
flask run
```

2. Open your browser and navigate to:

```
http://127.0.0.1:5000/
```

3. Follow the on-screen instructions to create your first video.

## Requirements

- Python 3.8 or higher
- Gemini API key (from Google AI Studio)
- SERP API key (from serpapi.com)
- FFmpeg (for video processing)

## Integration with ForgeTube Core

This frontend is designed to work with ForgeTube's core modules:

- `diffusion/scripts/generate_script.py`: For script generation
- `diffusion/scripts/generate_image_local.py`: For image generation
- `tts/scripts/generate_audio.py`: For audio generation
- `assembly/scripts/assembly_video.py`: For final video assembly

## Development

To contribute to this frontend:

1. Create a new branch for your feature or bug fix
2. Make your changes
3. Submit a pull request

## License

This project is licensed under the same terms as the main ForgeTube project.
10 changes: 10 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
flask==2.3.3
google-generativeai==0.3.1
serpapi==0.1.0
pysrt==1.1.2
moviepy==1.0.3
werkzeug==2.3.7
jinja2==3.1.2
itsdangerous==2.1.2
gunicorn==21.2.0
python-dotenv==1.0.0
Loading