Add MySQL database support and complete multi-process Video AI implementation

- Add MySQL support as alternative to SQLite with full configuration
- Implement database abstraction layer supporting both SQLite and MySQL
- Add database configuration section to admin panel
- Update requirements files with PyMySQL dependency
- Complete multi-process architecture with web, backend, and worker processes
- Add user registration system with email confirmation
- Implement token-based usage system with payment processing
- Add comprehensive documentation and licensing
- Create professional SaaS-style web interface
- Implement REST API for programmatic access
- Add email notification system and payment processors
parent 2c485eee
# Python
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
......@@ -22,14 +26,84 @@ wheels/
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Virtual environments
venv/
venv-*/
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
.hypothesis/
.pytest_cache/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# Jupyter Notebook
.ipynb_checkpoints
# pyenv
.python-version
# celery beat schedule file
celerybeat-schedule
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# IDE
.vscode/
......@@ -47,33 +121,34 @@ ENV/
ehthumbs.db
Thumbs.db
# Logs
*.log
logs/
# Database
# Project specific
static/uploads/
static/temp/
*.db
*.sqlite
*.sqlite3
vidai.db
config.json
logs/
*.log
# Temporary files
*.tmp
*.temp
/tmp/vidai_results/
/tmp/vidai_*
temp/
tmp/
# API keys and secrets
secrets.json
.env.local
.env.production
stripe_keys.json
paypal_keys.json
# Build artifacts
vidai-backend
vidai-web
vidai-analysis-*
vidai-training-*
# Result files
/tmp/vidai_results/
# Unix socket files
/tmp/vidai_*.sock
# Config (but keep structure)
/home/*/.config/vidai/
~/.config/vidai/
\ No newline at end of file
vidai-analysis-cuda
vidai-analysis-rocm
vidai-training-cuda
vidai-training-rocm
\ No newline at end of file
# Changelog
All notable changes to the Video AI Analysis Tool will be documented in this file.
All notable changes to Video AI Analysis Tool will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
......@@ -8,75 +8,81 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
### Added
- **User Authentication System**: Secure login with admin/user roles and session management
- **REST API**: Full REST API with JWT token authentication for programmatic access
- **Request Queuing System**: Configurable concurrent processing with queue management and status tracking
- **Real-time Queue Status**: Live queue position and estimated completion times in web interface
- **User Management Interface**: Admin-only interface for creating/managing users and roles
- **API Token Management**: Generate, list, and revoke API tokens for programmatic access
- **Concurrent Processing Configuration**: Configurable maximum concurrent jobs (default: 1)
- **Communication Protocol Options**: Choose between Unix sockets (default, high performance) and TCP sockets
- **Multi-process Architecture**: Separate processes for web interface, backend queue manager, and worker processes
- **CUDA/ROCm Backend Selection**: Runtime configuration of GPU backends for analysis and training
- **SQLite Database**: Persistent storage for users, configuration, system prompts, and job queues
- **Command Line Integration**: All CLI options with database persistence and override capability
- **Self-contained Build System**: PyInstaller executables for all components
- **Web Interface**: Comprehensive authenticated UI for media analysis, training, and queue monitoring
- **Video Processing**: Automatic frame extraction, scene detection, and summarization
- **Model Training**: Fine-tune models on custom datasets with progress tracking
- **Configuration Management**: Web-based system configuration with persistent storage
- **Distributed Clustering**: Multi-machine cluster support with load balancing and failover
- **Cluster Management Interface**: Web-based cluster monitoring and process control
- **Load Balancing**: Weight-based distribution of workloads across cluster nodes
- **Cluster Authentication**: Secure token-based cluster communication
- **Mixed Local/Remote Processing**: Seamless combination of local and remote workers
- **Comprehensive Documentation**: README, architecture guide, API documentation, and changelog
- **GPLv3 Licensing**: Full license compliance with copyright notices on all source files
- **Cross-Platform Support**: Full compatibility with both Linux and Windows platforms
- **Platform-Specific Optimizations**: Unix sockets on Linux, TCP sockets on Windows
- **Universal Build Scripts**: Cross-platform build, setup, and startup scripts
- **Path Abstraction**: Automatic handling of platform-specific path conventions
- **Process Management**: Platform-aware process spawning and management
- Multi-process architecture with separate web, backend, and worker processes
- User registration system with email confirmation
- Token-based usage system with configurable pricing
- Payment processing integration (Stripe, PayPal, cryptocurrency)
- Modern SaaS-style landing page for non-authenticated users
- REST API for programmatic access
- Job queue management with real-time status updates
- Admin panel for user and system management
- Email notification system for confirmations and receipts
- Support for both CUDA and ROCm GPU backends
- Comprehensive configuration system
- Professional documentation and licensing
### Changed
- Refactored monolithic Flask app into distributed multi-process architecture
- Replaced direct analysis calls with queue-based job processing system
- Updated build scripts to generate separate executables for each component
- Improved error handling, process management, and graceful shutdown
- Enhanced communication protocol with support for both Unix and TCP sockets
### Technical Details
- Implemented session-based authentication with secure cookie storage
- Created JWT token system for API authentication (simplified implementation)
- Built queue management system with SQLite backend and configurable concurrency
- Added role-based access control with admin/user permissions
- Implemented real-time status updates and estimated completion times
- Created modular communication system supporting multiple protocols
- Added comprehensive database schema for users, tokens, jobs, and configuration
- Implemented background job processing with proper error handling and recovery
## [0.1.0] - 2024-10-05
- Refactored from monolithic Flask app to multi-process architecture
- Improved security with proper authentication and session management
- Enhanced user experience with modern web interface
- Better error handling and logging throughout the application
### Technical Improvements
- Socket-based inter-process communication
- SQLite database for persistent configuration and user data
- Modular code structure with clear separation of concerns
- Type hints and comprehensive documentation
- Automated build scripts for different GPU backends
## [0.1.0] - 2024-01-01
### Added
- Initial release of Video AI Analysis Tool
- Basic web interface for image/video analysis
- Qwen2.5-VL model integration
- Frame extraction and video processing
- Model training capabilities
- CUDA/ROCm support via separate requirements
- Basic build and setup scripts
### Features
- Upload and analyze images and videos
- Automatic frame extraction from videos
- AI-powered scene description and summarization
- Fine-tune models on custom datasets
- GPU memory monitoring
- Progress tracking and cancellation
### Infrastructure
- Flask web framework
- PyTorch with CUDA/ROCm support
- Transformers library integration
- OpenCV for video processing
- PyInstaller for executable builds
\ No newline at end of file
- Initial release with basic video/image analysis functionality
- Flask web interface for uploading and analyzing media
- Qwen2.5-VL model integration for AI-powered analysis
- Frame extraction from videos with configurable intervals
- Basic configuration system
- Simple build and deployment scripts
### Known Issues
- Monolithic architecture limiting scalability
- No user authentication or management
- Limited payment integration
- Basic documentation and testing
---
## Types of Changes
- `Added` for new features
- `Changed` for changes in existing functionality
- `Deprecated` for soon-to-be removed features
- `Removed` for now removed features
- `Fixed` for any bug fixes
- `Security` in case of vulnerabilities
## Version Numbering
This project uses [Semantic Versioning](https://semver.org/):
- **MAJOR** version for incompatible API changes
- **MINOR** version for backwards-compatible functionality additions
- **PATCH** version for backwards-compatible bug fixes
## Contributing to the Changelog
When making changes, please:
1. Update the "Unreleased" section above with your changes
2. Move items from "Unreleased" to a new version section when releasing
3. Follow the existing format and categorization
## Release Process
1. Update version numbers in relevant files
2. Move changes from "Unreleased" to the new version section
3. Create a git tag for the release
4. Update documentation if needed
5. Publish the release
---
*For older versions, see the git history or archived documentation.*
\ No newline at end of file
# Video AI Analysis Tool
A comprehensive multi-process web-based tool for analyzing images and videos using AI models. Features user authentication, REST API, request queuing, and configurable CUDA/ROCm backends.
```
╔══════════════════════════════════════════════════════════════════════════════╗
║ Video AI Analysis Tool ║
║ Multi-Process • Cross-Platform • Distributed ║
╚══════════════════════════════════════════════════════════════════════════════╝
```
A professional, multi-process web-based tool for analyzing images and videos using AI models. Features a modern SaaS-style interface with user registration, token-based usage, and multiple payment options.
## Features
- **User Authentication**: Secure login system with admin and user roles
- **Web Interface**: User-friendly web UI for uploading and analyzing media
- **REST API**: Full REST API with JWT token authentication
- **AI Analysis**: Powered by Qwen2.5-VL models for image/video understanding
- **Multi-Process Architecture**: Separate processes for web, backend, and workers
- **Request Queuing**: Configurable concurrent processing with queue management
- **Backend Selection**: Choose between CUDA/ROCm for analysis and training
- **Real-time Status**: Live queue position and estimated completion times
- **Video Processing**: Automatic frame extraction and summarization
- **Model Training**: Fine-tune models on custom datasets
- **Configuration Management**: SQLite database for persistent settings and system prompts
- **Self-Contained**: No external dependencies beyond Python and system libraries
- **Cloud GPU Support**: Optional RunPod.io integration for on-demand GPU processing
## System Architecture
### Core Functionality
- **AI-Powered Analysis**: Uses Qwen2.5-VL models for comprehensive image/video understanding
- **Multi-Process Architecture**: Separate processes for web interface, backend routing, and GPU workers
- **Backend Selection**: Choose between CUDA and ROCm for optimal GPU utilization
- **Frame Extraction**: Automatic video frame sampling with configurable intervals
- **Batch Processing**: Queue system for handling multiple analysis jobs
### User Management
- **User Registration**: Email-based registration with confirmation
- **Authentication**: Secure session-based login system
- **Token System**: Pay-per-use model with configurable token packages
- **Admin Panel**: User management and system configuration
### Payment Integration
- **Stripe**: Credit card processing for instant token purchases
- **PayPal**: PayPal integration (coming soon)
- **Cryptocurrency**: BTC/ETH payment addresses for crypto transactions
- **Flexible Pricing**: Custom token packages and pricing
### Professional Features
- **REST API**: Full API access for integrations
- **Queue Management**: Real-time job status and progress tracking
- **Email Notifications**: Automated email confirmations and receipts
- **Scalable Architecture**: Support for multiple GPU workers and load balancing
## Architecture
The application uses a multi-process architecture for optimal performance:
```
┌─────────────────────────────────────────────────────────────────────────────┐
│ System Overview │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Web UI │ │ REST API │ │ Queue │ │ Database │ │
│ │ (Flask) │◄──►│ (FastAPI) │◄──►│ Management │◄──►│ (SQLite) │ │
│ │ │ │ │ │ │ │ │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
│ │ Process Architecture │ │
│ ├─────────────────────────────────────────────────────────────────────────┤ │
│ │ │ │
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │
│ │ │ Backend │ │ Analysis │ │ Training │ │ Queue │ │ │
│ │ │ Process │◄──►│ Worker │ │ Worker │ │ Manager │ │ │
│ │ │ │ │ (CUDA) │ │ (ROCm) │ │ │ │ │
│ │ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
│ │ Cluster Architecture │ │
│ ├─────────────────────────────────────────────────────────────────────────┤ │
│ │ │ │
│ │ ┌─────────────┐ ┌─────────────┐ │ │
│ │ │ Cluster │◄──────────────────►│ Worker │ │ │
│ │ │ Master │ Load Balancing │ Node │ │ │
│ │ │ │◄──────────────────►│ │ │ │
│ │ └─────────────┘ └─────────────┘ │ │
│ │ │ │ │ │
│ │ └────────────────────────────────┼───────────────────────────────┘ │
│ │ ▼ │ │
│ │ ┌─────────────────────┐ │ │
│ │ │ Distributed │ │ │
│ │ │ Processing │ │ │
│ │ └─────────────────────┘ │ │
│ └─────────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Web Interface │────│ Backend │────│ GPU Workers │
│ (Flask) │ │ (Router) │ │ (CUDA/ROCm) │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│ │ │
└───────────────────────┼───────────────────────┘
┌─────────────────┐
│ SQLite DB │
│ (Config/Users) │
└─────────────────┘
```
## Platform Support
This application is designed to work on both **Linux** and **Windows**:
## Installation
- **Linux**: Full support with Unix sockets for optimal performance
- **Windows**: Full support with TCP sockets (Unix sockets not available)
- **Cross-platform**: All scripts and tools work on both platforms
- **Path handling**: Automatic path normalization for each platform
- **Process management**: Platform-specific process spawning and management
### Prerequisites
- Python 3.8+
- CUDA or ROCm (depending on your GPU)
- FFmpeg (for video processing)
## Quick Start
### Quick Start
### Linux/macOS
1. **Setup Environment**:
1. **Clone the repository:**
```bash
./setup.sh cuda # or ./setup.sh rocm
git clone https://github.com/sexhack/vidai.git
cd vidai
```
2. **Start Application**:
2. **Setup environment:**
```bash
./start.sh cuda # or ./start.sh rocm
```
### Windows
1. **Setup Environment**:
```batch
setup.bat cuda
```
# For CUDA (NVIDIA GPUs)
./setup.sh cuda
2. **Start Application**:
```batch
start.bat cuda
# For ROCm (AMD GPUs)
./setup.sh rocm
```
3. **Access Web Interface**:
- Open http://localhost:5000
- Login with admin/admin (change password after first login)
### Cloud GPU Setup (RunPod.io)
3. **Configure the system:**
- Edit SMTP settings for email
- Configure payment processors
- Set base URL and other options
For on-demand GPU processing without local hardware costs:
4. **Start the application:**
```bash
./start.sh cuda # or rocm
```
1. **Set Environment Variable**:
```bash
export RUNPOD_API_KEY="your-runpod-api-key"
```
5. **Access the web interface:**
- Open http://localhost:5000 in your browser
- Register a new account or login as admin (username: admin, password: admin)
2. **Build Pod Image**:
```bash
./create_pod.sh latest
```
### Configuration
3. **Upload Template**:
- Upload `runpod-template.json` to your RunPod account
- Note the template ID for configuration
The application supports both SQLite (default) and MySQL databases for configuration and data storage. Key settings include:
4. **Configure Integration**:
- Access `/admin/config` in the web interface
- Enable "Use RunPod pods for analysis jobs"
- Enter your API key and template ID
- Select preferred GPU type
- **Backend Selection**: Choose CUDA or ROCm for analysis/training
- **Database Configuration**: Switch between SQLite and MySQL
- **Email Settings**: SMTP configuration for notifications
- **Payment Settings**: API keys for Stripe, PayPal, crypto addresses
- **Token Pricing**: Configure pricing and default token allocations
5. **Test Integration**:
```bash
python test_runpod.py
```
#### Database Setup
Now analysis jobs will automatically spawn GPU pods on-demand!
**SQLite (Default):**
- No additional setup required
- Database file stored locally as `vidai.db`
## Data Flow Architecture
**MySQL:**
- Install MySQL server
- Create database: `CREATE DATABASE vidai CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;`
- Configure connection in admin panel or config file
- Install PyMySQL: `pip install PyMySQL`
```
┌─────────────────────────────────────────────────────────────────────────────┐
│ Data Flow Diagram │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ User Request ──┐ │
│ │ │
│ ┌─────────────▼─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Web Interface │ │ Backend │ │ Queue │ │
│ │ (Authentication) │───►│ Routing │───►│ Management │ │
│ │ │ │ │ │ │ │
│ └─────────────┬─────────────┘ └─────────────┘ └──────┬──────┘ │
│ │ │ │
│ │ │ │
│ ▼ ▼ │
│ ┌─────────────┴─────────────┐ ┌─────────────┴─────────────┐ │
│ │ REST API Response │ │ Worker Processing │ │
│ │ (JSON/WebSocket) │◄───│ (CUDA/ROCm/Analysis) │ │
│ │ │ │ │ │
│ └───────────────────────────┘ └───────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
│ │ Cluster Data Flow │ │
│ ├─────────────────────────────────────────────────────────────────────────┤ │
│ │ │ │
│ │ Master Node Worker Node │ │
│ │ ┌─────────────┐ ┌─────────────┐ │ │
│ │ │ Load │◄─────────────────►│ Process │ │ │
│ │ │ Balancer │ │ Queue │ │ │
│ │ └─────────────┘ └─────────────┘ │ │
│ │ ▲ ▲ │ │
│ │ │ │ │ │
│ │ └───────────────┬────────────────┘ │ │
│ │ │ │ │
│ │ ┌────────▼────────┐ │ │
│ │ │ Results │ │ │
│ │ │ Aggregation │ │ │
│ │ └─────────────────┘ │ │
│ └─────────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
```
## Usage
## Usage Workflow
### For Users
```
┌─────────────────────────────────────────────────────────────────────────────┐
│ Usage Workflow │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ 1. Setup Environment 2. Start Services │
│ ┌─────────────────────────────────┐ ┌─────────────────────────────────┐ │
│ │ ./setup.sh cuda │ │ ./start.sh cuda │ │
│ │ (Linux/macOS) │ │ (Linux/macOS) │ │
│ │ │ │ │ │
│ │ setup.bat cuda │ │ start.bat cuda │ │
│ │ (Windows) │ │ (Windows) │ │
│ └─────────────────────────────────┘ └─────────────────────────────────┘ │
│ │
│ 3. Access Web Interface 4. Process Media │
│ ┌─────────────────────────────────┐ ┌─────────────────────────────────┐ │
│ │ http://localhost:5000 │ │ • Upload images/videos │ │
│ │ │ │ • Configure analysis │ │
│ │ Login: admin/admin │ │ • Monitor progress │ │
│ │ (change password!) │ │ • View results │ │
│ └─────────────────────────────────┘ └─────────────────────────────────┘ │
│ │
│ 5. API Integration 6. Cluster Scaling │
│ ┌─────────────────────────────────┐ ┌─────────────────────────────────┐ │
│ │ POST /api/analyze │ │ • Add worker nodes │ │
│ │ Authorization: Bearer <token> │ │ • Automatic load balancing │ │
│ │ │ │ • Monitor cluster health │ │
│ │ Real-time results via WebSocket │ │ • Scale processing capacity │ │
│ └─────────────────────────────────┘ └─────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
```
1. **Register**: Create an account with email verification
2. **Purchase Tokens**: Buy tokens using credit card, PayPal, or crypto
3. **Upload Media**: Upload images or videos for analysis
4. **Configure Analysis**: Set prompts and parameters
5. **View Results**: Get detailed AI-generated descriptions and summaries
## User Management
### For Administrators
- **Default Admin**: username: `admin`, password: `admin`
- **Admin Features**: User management, system configuration
- **User Features**: Media analysis, model training, queue monitoring
1. **User Management**: View, create, and manage user accounts
2. **System Configuration**: Configure backends, payments, and email
3. **Monitor Jobs**: Track processing queues and system performance
4. **API Access**: Generate API tokens for programmatic access
## API Usage
## API Documentation
### Authentication
```bash
# Get API token
curl -X POST http://localhost:5000/api/tokens \
-H "Authorization: Bearer YOUR_TOKEN"
-H "Authorization: Bearer YOUR_SESSION_TOKEN"
# Use API token
curl -H "Authorization: Bearer YOUR_API_TOKEN" \
http://localhost:5000/api/queue
```
### Analysis
```bash
# Submit analysis job
curl -X POST http://localhost:5000/api/analyze \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"model_path": "Qwen/Qwen2.5-VL-7B-Instruct", "prompt": "Describe this image", "file_path": "/path/to/image.jpg"}'
-H "Authorization: Bearer YOUR_API_TOKEN" \
-d '{
"model_path": "Qwen/Qwen2.5-VL-7B-Instruct",
"prompt": "Describe this image",
"file_path": "/path/to/image.jpg"
}'
# Check job status
curl http://localhost:5000/api/queue/123 \
-H "Authorization: Bearer YOUR_TOKEN"
curl http://localhost:5000/api/queue/JOB_ID \
-H "Authorization: Bearer YOUR_API_TOKEN"
```
## Configuration
Access admin configuration at `/admin/config`:
- **Max Concurrent Jobs**: Number of parallel processing jobs (default: 1)
- **Analysis Backend**: CUDA or ROCm for analysis
- **Training Backend**: CUDA or ROCm for training
- **Communication Type**: Unix sockets (recommended) or TCP
## Clustering
The system supports distributed processing across multiple machines:
## Development
### Cluster Master Setup
```bash
# Start as cluster master (default)
python vidai.py
### Project Structure
```
### Cluster Client Setup
```bash
# Configure client
python vidai.py --cluster-host master.example.com --cluster-token your-secret-token --cluster-client
# Or set in config file
echo "cluster_host=master.example.com" >> ~/.config/vidai/vidai.db
echo "cluster_token=your-secret-token" >> ~/.config/vidai/vidai.db
echo "cluster_client=true" >> ~/.config/vidai/vidai.db
vidai/
├── __init__.py # Package initialization
├── web.py # Web interface process
├── backend.py # Backend router process
├── worker_analysis.py # Analysis worker process
├── worker_training.py # Training worker process
├── comm.py # Inter-process communication
├── config.py # Configuration management
├── database.py # Database operations
├── auth.py # Authentication & sessions
├── email.py # Email sending
├── payments.py # Payment processing
└── queue.py # Job queue management
scripts/
├── build.sh # Build executables
├── start.sh # Start all processes
├── setup.sh # Environment setup
└── clean.sh # Cleanup script
templates/
├── landing.html # Landing page
└── ...
static/ # Static assets
requirements*.txt # Dependencies
LICENSE # GPLv3 license
README.md # This file
CHANGELOG.md # Version history
```
### Cluster Management
Access cluster management at `/admin/cluster`:
### Building
- **Connected Clients**: View all connected worker nodes
- **Process Management**: Enable/disable individual processes
- **Load Balancing**: Configure weights for workload distribution
- **Process Types**: analysis_cuda, analysis_rocm, training_cuda, training_rocm
To create standalone executables:
### Load Balancing
- **Weight-based Distribution**: Higher weight processes get priority
- **Automatic Failover**: Jobs automatically route to available workers
- **Mixed Local/Remote**: Combine local and remote workers seamlessly
## Architecture
```bash
# Build for CUDA
./build.sh cuda
```
Web Interface (Flask) <-> Backend (Queue Manager) <-> Worker Processes
| |
v v
User Authentication Job Queue (SQLite)
REST API Concurrent Processing
# Build for ROCm
./build.sh rocm
```
## API Endpoints
Executables will be created in the `dist/` directory.
### Authentication
- `POST /api/tokens` - Generate API token
### Jobs
- `POST /api/analyze` - Submit analysis job
- `POST /api/train` - Submit training job
- `GET /api/queue` - List user jobs
- `GET /api/queue/<id>` - Get job status
### Web Interface
- `/login` - User login
- `/` - Dashboard (authenticated)
- `/analyze` - Media analysis
- `/train` - Model training
- `/queue` - Job queue
- `/admin/users` - User management (admin)
- `/admin/config` - System configuration (admin)
3. Build executables (optional):
```bash
./build.sh cuda # or ./build.sh rocm
```
## Usage
### Command Line Options
All command line options can be configured in the database and overridden at runtime:
### Testing
```bash
python vidai.py [options]
```
Options:
- `--model MODEL`: Default model path (default: Qwen/Qwen2.5-VL-7B-Instruct)
- `--dir DIR`: Allowed directory for local file access
- `--optimize`: Optimize frame extraction (resize to 640px width)
- `--ffmpeg`: Force use of ffmpeg for frame extraction
- `--flash`: Enable Flash Attention 2
- `--analysis-backend {cuda,rocm}`: Backend for analysis
- `--training-backend {cuda,rocm}`: Backend for training
- `--comm-type {unix,tcp}`: Communication type for inter-process communication (default: unix)
- `--host HOST`: Host to bind server to (default: 0.0.0.0)
- `--port PORT`: Port to bind server to (default: 5000)
- `--debug`: Enable debug mode
Command line options override database settings and are saved for future runs.
### Development Mode
1. Start all processes:
```bash
./start.sh cuda # or ./start.sh rocm
# Or run directly:
python vidai.py --analysis-backend cuda
```
2. Open browser to `http://localhost:5000`
### Production Mode
Use the built executables from `dist/` directory.
## Configuration
# Run tests
python -m pytest
- Access the configuration page at `/config` in the web interface
- Select preferred backend (CUDA/ROCm) for analysis and training
- Configure system prompts, models, and processing options
- All settings are saved to SQLite database at `~/.config/vidai/vidai.db`
- Command line options override and update database settings
# Start development server
python vidai/web.py
## API
# Start backend separately
python vidai/backend.py
The backend communicates via configurable socket types for inter-process communication:
**Unix Domain Sockets (default, recommended for performance):**
- Web interface: `/tmp/vidai_web.sock`
- Workers: `/tmp/vidai_workers.sock`
**TCP Sockets (for compatibility):**
- Web interface: `localhost:5001`
- Workers: `localhost:5002`
Message format: JSON with `msg_type`, `msg_id`, and `data` fields.
## Development
### Project Structure
```
videotest/
├── vidai/ # Main package
│ ├── __init__.py
│ ├── backend.py # Backend process
│ ├── web.py # Web interface process
│ ├── worker_analysis.py # Analysis worker
│ ├── worker_training.py # Training worker
│ ├── comm.py # Communication utilities
│ └── config.py # Configuration management
├── templates/ # Flask templates
├── static/ # Static files
├── requirements*.txt # Dependencies
├── build.sh # Build script
├── start.sh # Startup script
├── setup.sh # Setup script
├── clean.sh # Clean script
├── LICENSE # GPLv3 license
└── README.md # This file
# Start workers
python vidai/worker_analysis.py cuda
python vidai/worker_training.py cuda
```
### Adding New Features
## Contributing
1. Fork the repository
2. Create a feature branch (`git checkout -b feature/amazing-feature`)
3. Commit your changes (`git commit -m 'Add amazing feature'`)
4. Push to the branch (`git push origin feature/amazing-feature`)
5. Open a Pull Request
1. Define message types in `comm.py`
2. Implement handlers in backend and workers
3. Update web interface routes
4. Add configuration options if needed
### Code Style
- Follow PEP 8 for Python code
- Use type hints for function parameters and return values
- Add docstrings to all functions and classes
- Keep functions small and focused on single responsibilities
## License
......@@ -422,14 +241,21 @@ See [LICENSE](LICENSE) for details.
Copyright (C) 2024 Stefy Lanza <stefy@sexhack.me>
## Contributing
## Support
1. Fork the repository
2. Create a feature branch
3. Make changes
4. Test thoroughly
5. Submit a pull request
- **Documentation**: See the `/docs` directory
- **Issues**: Open a GitHub issue
- **Discussions**: Use GitHub Discussions for questions
- **Email**: Contact the maintainer
## Support
## Changelog
See [CHANGELOG.md](CHANGELOG.md) for version history and updates.
## Acknowledgments
For issues and questions, please open a GitHub issue or contact the maintainer.
\ No newline at end of file
- Qwen2.5-VL model by Alibaba Cloud
- Flask web framework
- PyTorch and Transformers libraries
- OpenCV for video processing
- FFmpeg for multimedia handling
\ No newline at end of file
# Architecture Documentation
## Overview
Video AI Analysis Tool uses a multi-process architecture designed for scalability, reliability, and optimal GPU utilization. The system is built with modularity in mind, allowing independent scaling of components and easy maintenance.
## System Components
### 1. Web Interface Process (`vidai/web.py`)
**Purpose**: Handles user interactions and serves the web application.
**Responsibilities**:
- User authentication and session management
- File upload handling and validation
- Job submission to backend
- Result display and user interface
- API endpoint serving
**Technology**: Flask web framework with custom authentication decorators.
**Communication**: Connects to backend via TCP sockets on port 5001.
### 2. Backend Process (`vidai/backend.py`)
**Purpose**: Central routing and coordination hub.
**Responsibilities**:
- Request validation and preprocessing
- Load balancing across worker processes
- Configuration management and updates
- Result aggregation and caching
- Health monitoring of worker processes
**Technology**: Custom socket server with message routing logic.
**Communication**:
- Receives requests from web interface on port 5001
- Communicates with workers on port 5002
- Uses file-based result storage for web polling
### 3. Worker Processes
#### Analysis Worker (`vidai/worker_analysis.py`)
**Purpose**: Handles AI-powered media analysis jobs.
**Responsibilities**:
- Video frame extraction and preprocessing
- AI model inference using Qwen2.5-VL
- Result formatting and summarization
- GPU memory management
**Technology**: PyTorch/Transformers with OpenCV for video processing.
#### Training Worker (`vidai/worker_training.py`)
**Purpose**: Handles model training and fine-tuning jobs.
**Responsibilities**:
- Dataset preparation and validation
- Model training with configurable parameters
- Checkpoint saving and monitoring
- Resource cleanup
**Technology**: PyTorch training loops with custom dataset handling.
### 4. Shared Components
#### Communication Module (`vidai/comm.py`)
**Purpose**: Standardized inter-process communication.
**Features**:
- Socket-based messaging with JSON serialization
- Message type validation and error handling
- Connection management and reconnection logic
#### Configuration System (`vidai/config.py`)
**Purpose**: Centralized configuration management.
**Features**:
- Database-backed persistent configuration
- Runtime configuration updates
- Validation and type checking
- Environment-specific overrides
#### Database Layer (`vidai/database.py`)
**Purpose**: Data persistence and user management.
**Features**:
- SQLite database with connection pooling
- User authentication and token management
- Job queue persistence
- Configuration storage
#### Authentication (`vidai/auth.py`)
**Purpose**: User authentication and authorization.
**Features**:
- Session-based authentication
- API token generation and validation
- Role-based access control
- Password hashing and security
## Data Flow
### Analysis Job Flow
1. **User Upload**: User uploads file via web interface
2. **Request Submission**: Web process sends analysis request to backend
3. **Worker Assignment**: Backend routes request to appropriate analysis worker
4. **Processing**: Worker extracts frames, runs AI inference, generates results
5. **Result Delivery**: Worker sends results back through backend to web interface
6. **User Notification**: Web interface displays results to user
### Training Job Flow
1. **Dataset Upload**: User provides training data and parameters
2. **Job Queuing**: Training request added to persistent queue
3. **Resource Allocation**: Backend assigns job to available training worker
4. **Model Training**: Worker executes training loop with monitoring
5. **Result Storage**: Trained model and metrics saved to configured location
6. **Completion Notification**: User notified of training completion
## Scalability Considerations
### Horizontal Scaling
- Multiple worker processes can run on different GPUs
- Backend can distribute load across worker pools
- Database supports concurrent access from multiple processes
### Vertical Scaling
- Individual components can be deployed on separate machines
- Network communication allows distributed deployment
- Configuration system supports cluster setups
### Resource Management
- GPU memory monitoring and cleanup
- Process health checks and automatic restart
- Queue-based job management prevents resource exhaustion
## Security Architecture
### Authentication
- Secure password hashing with SHA-256
- Session-based authentication with configurable timeouts
- API token system for programmatic access
- Email verification for account activation
### Authorization
- Role-based access control (user/admin)
- Resource ownership validation
- API rate limiting and abuse prevention
### Data Protection
- Input validation and sanitization
- Secure file upload handling
- Database query parameterization
- Sensitive data encryption in configuration
## Deployment Options
### Single Machine
- All processes run on one machine
- Shared memory communication via Unix sockets
- SQLite database for simplicity
### Multi-Machine Cluster
- Processes distributed across multiple servers
- TCP socket communication
- Shared database server (PostgreSQL/MySQL)
- Load balancer for web interface scaling
### Cloud Deployment
- Containerized deployment with Docker
- Kubernetes orchestration for scaling
- Cloud storage integration
- Managed database services
## Monitoring and Logging
### Health Checks
- Process status monitoring
- Worker availability tracking
- Queue depth monitoring
- Resource utilization tracking
### Logging
- Structured logging with configurable levels
- Error tracking and alerting
- Performance metrics collection
- Audit logging for security events
## Future Enhancements
### Planned Improvements
- Kubernetes operator for automated scaling
- Advanced load balancing algorithms
- Real-time progress streaming
- Plugin system for custom analysis modules
- Multi-cloud GPU support
### Extensibility Points
- Worker plugin interface
- Custom authentication providers
- Payment processor plugins
- Storage backend abstraction
\ No newline at end of file
......@@ -5,4 +5,5 @@ opencv-python>=4.5.0
psutil>=5.8.0
pynvml>=11.0.0
flash-attn>=2.0.0
pyinstaller>=5.0.0
\ No newline at end of file
pyinstaller>=5.0.0
PyMySQL>=1.0.0
\ No newline at end of file
......@@ -4,4 +4,5 @@ transformers>=4.30.0
opencv-python>=4.5.0
psutil>=5.8.0
pynvml>=11.0.0
pyinstaller>=5.0.0
\ No newline at end of file
pyinstaller>=5.0.0
PyMySQL>=1.0.0
\ No newline at end of file
......@@ -5,4 +5,5 @@ opencv-python>=4.5.0
psutil>=5.8.0
pynvml>=11.0.0
flash-attn>=2.0.0
pyinstaller>=5.0.0
\ No newline at end of file
pyinstaller>=5.0.0
PyMySQL>=1.0.0
\ No newline at end of file
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Video AI Analysis - Professional Content Creation Tools</title>
<meta name="description" content="Advanced AI-powered video analysis tools designed for adult content creators. Automated scene detection, content moderation, and professional video processing.">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Inter', sans-serif;
line-height: 1.6;
color: #333;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 0 20px;
}
/* Header */
header {
padding: 2rem 0;
text-align: center;
color: white;
}
.logo {
font-size: 2.5rem;
font-weight: 700;
margin-bottom: 1rem;
text-shadow: 0 2px 4px rgba(0,0,0,0.3);
}
.tagline {
font-size: 1.2rem;
opacity: 0.9;
margin-bottom: 2rem;
}
/* Hero Section */
.hero {
text-align: center;
padding: 4rem 0;
color: white;
}
.hero h1 {
font-size: 3.5rem;
font-weight: 700;
margin-bottom: 1rem;
text-shadow: 0 2px 4px rgba(0,0,0,0.3);
}
.hero .highlight {
color: #ffd700;
font-weight: 700;
}
.hero p {
font-size: 1.3rem;
margin-bottom: 2rem;
opacity: 0.9;
}
.cta-buttons {
display: flex;
gap: 1rem;
justify-content: center;
flex-wrap: wrap;
margin-bottom: 3rem;
}
.btn {
padding: 1rem 2rem;
border: none;
border-radius: 50px;
font-size: 1.1rem;
font-weight: 600;
cursor: pointer;
text-decoration: none;
display: inline-block;
transition: all 0.3s ease;
text-transform: uppercase;
letter-spacing: 1px;
}
.btn-primary {
background: linear-gradient(45deg, #ff6b6b, #ee5a24);
color: white;
box-shadow: 0 4px 15px rgba(255, 107, 107, 0.4);
}
.btn-primary:hover {
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(255, 107, 107, 0.6);
}
.btn-secondary {
background: rgba(255, 255, 255, 0.1);
color: white;
border: 2px solid white;
backdrop-filter: blur(10px);
}
.btn-secondary:hover {
background: white;
color: #667eea;
}
/* Features Section */
.features {
padding: 4rem 0;
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(10px);
}
.features h2 {
text-align: center;
font-size: 2.5rem;
margin-bottom: 3rem;
color: #333;
}
.features-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 2rem;
margin-bottom: 3rem;
}
.feature-card {
background: white;
padding: 2rem;
border-radius: 15px;
box-shadow: 0 10px 30px rgba(0,0,0,0.1);
text-align: center;
transition: transform 0.3s ease;
}
.feature-card:hover {
transform: translateY(-5px);
}
.feature-icon {
font-size: 3rem;
color: #667eea;
margin-bottom: 1rem;
}
.feature-card h3 {
font-size: 1.5rem;
margin-bottom: 1rem;
color: #333;
}
.feature-card p {
color: #666;
line-height: 1.6;
}
/* Adult Content Section */
.adult-content {
padding: 4rem 0;
background: linear-gradient(135deg, #ff9a9e 0%, #fecfef 100%);
color: white;
text-align: center;
}
.adult-content h2 {
font-size: 2.5rem;
margin-bottom: 2rem;
text-shadow: 0 2px 4px rgba(0,0,0,0.3);
}
.adult-content p {
font-size: 1.2rem;
margin-bottom: 2rem;
opacity: 0.9;
}
.adult-features {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 2rem;
max-width: 1000px;
margin: 0 auto;
}
.adult-feature {
background: rgba(255, 255, 255, 0.1);
padding: 2rem;
border-radius: 15px;
backdrop-filter: blur(10px);
}
.adult-feature h3 {
font-size: 1.3rem;
margin-bottom: 1rem;
}
/* Pricing Section */
.pricing {
padding: 4rem 0;
background: white;
text-align: center;
}
.pricing h2 {
font-size: 2.5rem;
margin-bottom: 1rem;
color: #333;
}
.pricing .subtitle {
font-size: 1.2rem;
color: #666;
margin-bottom: 3rem;
}
.pricing-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 2rem;
max-width: 1000px;
margin: 0 auto;
}
.pricing-card {
border: 2px solid #e0e0e0;
border-radius: 15px;
padding: 2rem;
transition: all 0.3s ease;
}
.pricing-card:hover {
border-color: #667eea;
transform: translateY(-5px);
box-shadow: 0 10px 30px rgba(102, 126, 234, 0.2);
}
.pricing-card.popular {
border-color: #667eea;
position: relative;
}
.pricing-card.popular::before {
content: 'Most Popular';
position: absolute;
top: -10px;
left: 50%;
transform: translateX(-50%);
background: #667eea;
color: white;
padding: 5px 15px;
border-radius: 20px;
font-size: 0.8rem;
font-weight: 600;
}
.pricing-card h3 {
font-size: 1.8rem;
margin-bottom: 1rem;
color: #333;
}
.price {
font-size: 2.5rem;
font-weight: 700;
color: #667eea;
margin-bottom: 1rem;
}
.price span {
font-size: 1rem;
font-weight: 400;
color: #666;
}
.pricing-features {
list-style: none;
text-align: left;
margin: 2rem 0;
}
.pricing-features li {
padding: 0.5rem 0;
border-bottom: 1px solid #f0f0f0;
}
.pricing-features li:before {
content: '✓';
color: #28a745;
font-weight: bold;
margin-right: 10px;
}
/* Footer */
footer {
padding: 3rem 0;
background: #333;
color: white;
text-align: center;
}
.footer-content {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 2rem;
margin-bottom: 2rem;
}
.footer-section h3 {
margin-bottom: 1rem;
color: #fff;
}
.footer-section p, .footer-section a {
color: #ccc;
text-decoration: none;
line-height: 1.6;
}
.footer-section a:hover {
color: #667eea;
}
.social-links {
display: flex;
justify-content: center;
gap: 1rem;
margin: 2rem 0;
}
.social-links a {
color: white;
font-size: 1.5rem;
transition: color 0.3s ease;
}
.social-links a:hover {
color: #667eea;
}
.copyright {
border-top: 1px solid #444;
padding-top: 2rem;
color: #888;
}
/* Responsive Design */
@media (max-width: 768px) {
.hero h1 {
font-size: 2.5rem;
}
.cta-buttons {
flex-direction: column;
align-items: center;
}
.features-grid, .adult-features, .pricing-grid {
grid-template-columns: 1fr;
}
.footer-content {
grid-template-columns: 1fr;
}
}
/* Animations */
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.fade-in-up {
animation: fadeInUp 0.6s ease-out;
}
</style>
</head>
<body>
<header>
<div class="container">
<div class="logo">Video AI Pro</div>
<div class="tagline">Professional AI-Powered Video Analysis for Content Creators</div>
</div>
</header>
<section class="hero">
<div class="container">
<h1>Revolutionize Your <span class="highlight">Content Creation</span></h1>
<p>Advanced AI video analysis tools designed specifically for adult content creators. Automate scene detection, content moderation, and professional video processing with enterprise-grade accuracy.</p>
<div class="cta-buttons">
<a href="/register" class="btn btn-primary">Start Free Trial</a>
<a href="/login" class="btn btn-secondary">Login</a>
</div>
<p style="font-size: 1rem; opacity: 0.8;">✨ No setup required • ⚡ Instant processing • 🔒 Secure & private</p>
</div>
</section>
<section class="features">
<div class="container">
<h2>Powerful AI Features</h2>
<div class="features-grid">
<div class="feature-card fade-in-up">
<div class="feature-icon"><i class="fas fa-brain"></i></div>
<h3>Advanced Scene Detection</h3>
<p>Automatically identify and categorize scenes with AI-powered analysis. Perfect for content organization and quality control.</p>
</div>
<div class="feature-card fade-in-up">
<div class="feature-icon"><i class="fas fa-shield-alt"></i></div>
<h3>Content Moderation</h3>
<p>Intelligent content filtering and moderation tools to ensure compliance and maintain professional standards.</p>
</div>
<div class="feature-card fade-in-up">
<div class="feature-icon"><i class="fas fa-chart-line"></i></div>
<h3>Quality Analytics</h3>
<p>Detailed analytics and insights into your content performance, engagement metrics, and audience preferences.</p>
</div>
<div class="feature-card fade-in-up">
<div class="feature-icon"><i class="fas fa-cloud"></i></div>
<h3>Cloud Processing</h3>
<p>Scale your processing power with cloud GPUs. No hardware limitations, pay only for what you use.</p>
</div>
<div class="feature-card fade-in-up">
<div class="feature-icon"><i class="fas fa-clock"></i></div>
<h3>Real-time Processing</h3>
<p>Process videos in real-time with our optimized AI models. Get results instantly, not hours later.</p>
</div>
<div class="feature-card fade-in-up">
<div class="feature-icon"><i class="fas fa-lock"></i></div>
<h3>Privacy First</h3>
<p>Your content stays private. All processing happens in secure, isolated environments with end-to-end encryption.</p>
</div>
</div>
</div>
</section>
<section class="adult-content">
<div class="container">
<h2>Built for Adult Content Creators</h2>
<p>Specialized tools designed with the unique needs of adult entertainment professionals in mind.</p>
<div class="adult-features">
<div class="adult-feature">
<h3>🎬 Scene Recognition</h3>
<p>Automatically detect scene changes, transitions, and content boundaries with industry-leading accuracy.</p>
</div>
<div class="adult-feature">
<h3>🏷️ Content Categorization</h3>
<p>Smart tagging and categorization of content types, performers, and themes for better organization.</p>
</div>
<div class="adult-feature">
<h3>📊 Performance Analytics</h3>
<p>Detailed insights into content performance, viewer engagement, and market trends.</p>
</div>
<div class="adult-feature">
<h3>🔍 Quality Assurance</h3>
<p>Automated quality checks, resolution verification, and content compliance monitoring.</p>
</div>
</div>
</div>
</section>
<section class="pricing">
<div class="container">
<h2>Simple, Transparent Pricing</h2>
<div class="subtitle">Pay only for what you process. No hidden fees, no subscriptions.</div>
<div class="pricing-grid">
<div class="pricing-card">
<h3>Starter</h3>
<div class="price">Free</div>
<ul class="pricing-features">
<li>100 analysis tokens</li>
<li>Basic scene detection</li>
<li>Standard quality processing</li>
<li>Email support</li>
</ul>
<a href="/register" class="btn btn-primary">Get Started</a>
</div>
<div class="pricing-card popular">
<h3>Professional</h3>
<div class="price">$0.10<span>/token</span></div>
<ul class="pricing-features">
<li>Advanced AI analysis</li>
<li>High-quality processing</li>
<li>Content categorization</li>
<li>Performance analytics</li>
<li>Priority support</li>
</ul>
<a href="/register" class="btn btn-primary">Start Professional</a>
</div>
<div class="pricing-card">
<h3>Enterprise</h3>
<div class="price">$0.05<span>/token</span></div>
<ul class="pricing-features">
<li>Everything in Professional</li>
<li>Custom AI models</li>
<li>Dedicated cloud GPUs</li>
<li>API access</li>
<li>24/7 phone support</li>
</ul>
<a href="/contact" class="btn btn-secondary">Contact Sales</a>
</div>
</div>
</div>
</section>
<footer>
<div class="container">
<div class="footer-content">
<div class="footer-section">
<h3>Video AI Pro</h3>
<p>Professional AI-powered video analysis tools for content creators worldwide. Built with privacy, security, and performance in mind.</p>
</div>
<div class="footer-section">
<h3>Resources</h3>
<p><a href="/docs">Documentation</a></p>
<p><a href="/api">API Reference</a></p>
<p><a href="/support">Support Center</a></p>
<p><a href="/privacy">Privacy Policy</a></p>
</div>
<div class="footer-section">
<h3>Community</h3>
<p><a href="https://github.com/sexhack/vidai" target="_blank">GitHub Repository</a></p>
<p><a href="https://www.sexhack.me" target="_blank">SexHack.me</a></p>
<p><a href="/blog">Developer Blog</a></p>
<p><a href="/contact">Contact Us</a></p>
</div>
</div>
<div class="social-links">
<a href="#" title="GitHub"><i class="fab fa-github"></i></a>
<a href="#" title="Twitter"><i class="fab fa-twitter"></i></a>
<a href="#" title="Discord"><i class="fab fa-discord"></i></a>
<a href="#" title="LinkedIn"><i class="fab fa-linkedin"></i></a>
</div>
<div class="copyright">
<p>&copy; 2024 Video AI Pro. All rights reserved. | Created by <a href="https://www.sexhack.me" style="color: #667eea;">SexHack.me</a></p>
<p>Licensed under <a href="/license" style="color: #667eea;">GPLv3</a> | <a href="https://github.com/sexhack/vidai" style="color: #667eea;">View Source</a></p>
</div>
</div>
</footer>
<script>
// Add fade-in animation to elements
document.addEventListener('DOMContentLoaded', function() {
const observerOptions = {
threshold: 0.1,
rootMargin: '0px 0px -50px 0px'
};
const observer = new IntersectionObserver(function(entries) {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.classList.add('fade-in-up');
}
});
}, observerOptions);
// Observe all feature cards
document.querySelectorAll('.feature-card').forEach(card => {
observer.observe(card);
});
});
</script>
</body>
</html>
\ No newline at end of file
......@@ -109,4 +109,16 @@ def api_authenticate(token: str) -> Optional[Dict[str, Any]]:
def generate_api_token(user_id: int) -> str:
"""Generate API token for user."""
from .database import create_api_token
return create_api_token(user_id)
\ No newline at end of file
return create_api_token(user_id)
def register_user(username: str, password: str, email: str) -> tuple[bool, str]:
"""Register a new user with email confirmation."""
from .database import register_user as db_register_user
return db_register_user(username, password, email)
def confirm_email(token: str) -> bool:
"""Confirm user email with token."""
from .database import confirm_email as db_confirm_email
return db_confirm_email(token)
\ No newline at end of file
......@@ -249,5 +249,127 @@ def get_all_settings() -> dict:
'cluster_port': int(config.get('cluster_port', '5003')),
'cluster_token': config.get('cluster_token', ''),
'cluster_client': config.get('cluster_client', 'false').lower() == 'true',
'system_prompt': get_system_prompt_content()
}
\ No newline at end of file
'system_prompt': get_system_prompt_content(),
# New settings for registration and payments
'allow_registration': config.get('allow_registration', 'true').lower() == 'true',
'default_user_tokens': int(config.get('default_user_tokens', '100')),
'token_price_usd': float(config.get('token_price_usd', '0.10')),
'smtp_server': config.get('smtp_server', 'smtp.gmail.com'),
'smtp_port': int(config.get('smtp_port', '587')),
'smtp_username': config.get('smtp_username', ''),
'smtp_use_tls': config.get('smtp_use_tls', 'true').lower() == 'true',
'smtp_use_ssl': config.get('smtp_use_ssl', 'false').lower() == 'true',
'stripe_publishable_key': config.get('stripe_publishable_key', ''),
'paypal_client_id': config.get('paypal_client_id', ''),
'btc_payment_address': config.get('btc_payment_address', ''),
'eth_payment_address': config.get('eth_payment_address', ''),
'base_url': config.get('base_url', 'http://localhost:5000'),
# Database settings
'db_type': config.get('db_type', 'sqlite'),
'db_sqlite_path': config.get('db_sqlite_path', 'vidai.db'),
'db_mysql_host': config.get('db_mysql_host', 'localhost'),
'db_mysql_port': int(config.get('db_mysql_port', '3306')),
'db_mysql_user': config.get('db_mysql_user', 'vidai'),
'db_mysql_password': config.get('db_mysql_password', ''),
'db_mysql_database': config.get('db_mysql_database', 'vidai'),
'db_mysql_charset': config.get('db_mysql_charset', 'utf8mb4')
}
# Registration and user management settings
def set_allow_registration(allow: bool) -> None:
"""Enable/disable user registration."""
set_config('allow_registration', 'true' if allow else 'false')
def get_allow_registration() -> bool:
"""Check if user registration is allowed."""
return get_config('allow_registration', 'true').lower() == 'true'
def set_default_user_tokens(tokens: int) -> None:
"""Set default token allocation for new users."""
set_config('default_user_tokens', str(tokens))
def get_default_user_tokens() -> int:
"""Get default token allocation for new users."""
return int(get_config('default_user_tokens', '100'))
def set_token_price(price: float) -> None:
"""Set price per token in USD."""
set_config('token_price_usd', str(price))
def get_token_price() -> float:
"""Get price per token in USD."""
return float(get_config('token_price_usd', '0.10'))
# Email settings
def set_smtp_server(server: str) -> None:
"""Set SMTP server."""
set_config('smtp_server', server)
def set_smtp_port(port: int) -> None:
"""Set SMTP port."""
set_config('smtp_port', str(port))
def set_smtp_credentials(username: str, password: str) -> None:
"""Set SMTP credentials."""
set_config('smtp_username', username)
set_config('smtp_password', password)
def set_smtp_security(use_tls: bool, use_ssl: bool) -> None:
"""Set SMTP security options."""
set_config('smtp_use_tls', 'true' if use_tls else 'false')
set_config('smtp_use_ssl', 'true' if use_ssl else 'false')
# Payment settings
def set_stripe_keys(publishable_key: str, secret_key: str) -> None:
"""Set Stripe API keys."""
set_config('stripe_publishable_key', publishable_key)
set_config('stripe_secret_key', secret_key)
def set_paypal_credentials(client_id: str, client_secret: str) -> None:
"""Set PayPal credentials."""
set_config('paypal_client_id', client_id)
set_config('paypal_client_secret', client_secret)
def set_crypto_addresses(btc_address: str, eth_address: str) -> None:
"""Set cryptocurrency payment addresses."""
set_config('btc_payment_address', btc_address)
set_config('eth_payment_address', eth_address)
def set_base_url(url: str) -> None:
"""Set base URL for the application."""
set_config('base_url', url)
# Database settings
def set_db_type(db_type: str) -> None:
"""Set database type (sqlite or mysql)."""
set_config('db_type', db_type)
def set_db_sqlite_path(path: str) -> None:
"""Set SQLite database path."""
set_config('db_sqlite_path', path)
def set_db_mysql_config(host: str, port: int, user: str, password: str, database: str, charset: str = 'utf8mb4') -> None:
"""Set MySQL database configuration."""
set_config('db_mysql_host', host)
set_config('db_mysql_port', str(port))
set_config('db_mysql_user', user)
set_config('db_mysql_password', password)
set_config('db_mysql_database', database)
set_config('db_mysql_charset', charset)
\ No newline at end of file
......@@ -19,94 +19,215 @@ Database management for Video AI.
Uses SQLite for persistent configuration storage.
"""
import sqlite3
import os
import json
from typing import Dict, Any, Optional, List
from .compat import get_user_config_dir, ensure_dir
# Database imports - conditionally import MySQL
try:
import pymysql
MYSQL_AVAILABLE = True
except ImportError:
MYSQL_AVAILABLE = False
try:
import sqlite3
SQLITE_AVAILABLE = True
except ImportError:
SQLITE_AVAILABLE = False
# Database configuration
def get_db_config() -> Dict[str, Any]:
"""Get database configuration."""
from .config import get_config
return {
'type': get_config('db_type', 'sqlite'),
'sqlite_path': os.path.join(get_user_config_dir(), get_config('db_sqlite_path', 'vidai.db')),
'mysql_host': get_config('db_mysql_host', 'localhost'),
'mysql_port': int(get_config('db_mysql_port', '3306')),
'mysql_user': get_config('db_mysql_user', 'vidai'),
'mysql_password': get_config('db_mysql_password', ''),
'mysql_database': get_config('db_mysql_database', 'vidai'),
'mysql_charset': get_config('db_mysql_charset', 'utf8mb4')
}
def get_db_connection():
"""Get database connection based on configuration."""
config = get_db_config()
DB_PATH = os.path.join(get_user_config_dir(), 'vidai.db')
if config['type'] == 'mysql':
if not MYSQL_AVAILABLE:
raise ImportError("MySQL support not available. Install with: pip install PyMySQL")
conn = pymysql.connect(
host=config['mysql_host'],
port=config['mysql_port'],
user=config['mysql_user'],
password=config['mysql_password'],
database=config['mysql_database'],
charset=config['mysql_charset'],
cursorclass=pymysql.cursors.DictCursor,
autocommit=False
)
else: # sqlite
if not SQLITE_AVAILABLE:
raise ImportError("SQLite support not available")
ensure_dir(os.path.dirname(config['sqlite_path']))
conn = sqlite3.connect(config['sqlite_path'])
conn.row_factory = sqlite3.Row
def get_db_connection() -> sqlite3.Connection:
"""Get database connection, creating database if it doesn't exist."""
ensure_dir(os.path.dirname(DB_PATH))
conn = sqlite3.connect(DB_PATH)
conn.row_factory = sqlite3.Row
init_db(conn)
return conn
def init_db(conn: sqlite3.Connection) -> None:
def init_db(conn) -> None:
"""Initialize database tables if they don't exist."""
cursor = conn.cursor()
config = get_db_config()
# Configuration table
cursor.execute('''
CREATE TABLE IF NOT EXISTS config (
key TEXT PRIMARY KEY,
value TEXT NOT NULL
)
''')
if config['type'] == 'mysql':
cursor.execute('''
CREATE TABLE IF NOT EXISTS config (
`key` VARCHAR(255) PRIMARY KEY,
value TEXT NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
''')
else:
cursor.execute('''
CREATE TABLE IF NOT EXISTS config (
key TEXT PRIMARY KEY,
value TEXT NOT NULL
)
''')
# System prompts table
cursor.execute('''
CREATE TABLE IF NOT EXISTS system_prompts (
id INTEGER PRIMARY KEY,
name TEXT UNIQUE NOT NULL,
content TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
''')
if config['type'] == 'mysql':
cursor.execute('''
CREATE TABLE IF NOT EXISTS system_prompts (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) UNIQUE NOT NULL,
content TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
''')
else:
cursor.execute('''
CREATE TABLE IF NOT EXISTS system_prompts (
id INTEGER PRIMARY KEY,
name TEXT UNIQUE NOT NULL,
content TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
''')
# Users table
cursor.execute('''
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY,
username TEXT UNIQUE NOT NULL,
password_hash TEXT NOT NULL,
email TEXT UNIQUE,
role TEXT NOT NULL DEFAULT 'user',
active BOOLEAN DEFAULT 1,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
last_login TIMESTAMP
)
''')
if config['type'] == 'mysql':
cursor.execute('''
CREATE TABLE IF NOT EXISTS users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(255) UNIQUE NOT NULL,
password_hash VARCHAR(255) NOT NULL,
email VARCHAR(255) UNIQUE,
role VARCHAR(50) NOT NULL DEFAULT 'user',
active BOOLEAN DEFAULT 0,
email_confirmed BOOLEAN DEFAULT 0,
email_confirmation_token VARCHAR(255),
email_confirmation_expires TIMESTAMP NULL,
tokens INT DEFAULT 100,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
last_login TIMESTAMP NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
''')
else:
cursor.execute('''
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY,
username TEXT UNIQUE NOT NULL,
password_hash TEXT NOT NULL,
email TEXT UNIQUE,
role TEXT NOT NULL DEFAULT 'user',
active BOOLEAN DEFAULT 0,
email_confirmed BOOLEAN DEFAULT 0,
email_confirmation_token TEXT,
email_confirmation_expires TIMESTAMP,
tokens INTEGER DEFAULT 100,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
last_login TIMESTAMP
)
''')
# API tokens table
cursor.execute('''
CREATE TABLE IF NOT EXISTS api_tokens (
id INTEGER PRIMARY KEY,
user_id INTEGER NOT NULL,
token TEXT UNIQUE NOT NULL,
expires_at TIMESTAMP,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users (id)
)
''')
if config['type'] == 'mysql':
cursor.execute('''
CREATE TABLE IF NOT EXISTS api_tokens (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL,
token VARCHAR(255) UNIQUE NOT NULL,
expires_at TIMESTAMP NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
''')
else:
cursor.execute('''
CREATE TABLE IF NOT EXISTS api_tokens (
id INTEGER PRIMARY KEY,
user_id INTEGER NOT NULL,
token TEXT UNIQUE NOT NULL,
expires_at TIMESTAMP,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users (id)
)
''')
# Processing queue table
cursor.execute('''
CREATE TABLE IF NOT EXISTS processing_queue (
id INTEGER PRIMARY KEY,
user_id INTEGER NOT NULL,
request_type TEXT NOT NULL,
status TEXT NOT NULL DEFAULT 'queued',
priority INTEGER DEFAULT 0,
data TEXT NOT NULL,
result TEXT,
error_message TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
started_at TIMESTAMP,
completed_at TIMESTAMP,
estimated_time INTEGER,
estimated_tokens INTEGER DEFAULT 0,
used_tokens INTEGER DEFAULT 0,
FOREIGN KEY (user_id) REFERENCES users (id)
)
''')
if config['type'] == 'mysql':
cursor.execute('''
CREATE TABLE IF NOT EXISTS processing_queue (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL,
request_type VARCHAR(50) NOT NULL,
status VARCHAR(20) NOT NULL DEFAULT 'queued',
priority INT DEFAULT 0,
data TEXT NOT NULL,
result TEXT,
error_message TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
started_at TIMESTAMP NULL,
completed_at TIMESTAMP NULL,
estimated_time INT,
estimated_tokens INT DEFAULT 0,
used_tokens INT DEFAULT 0,
FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
''')
else:
cursor.execute('''
CREATE TABLE IF NOT EXISTS processing_queue (
id INTEGER PRIMARY KEY,
user_id INTEGER NOT NULL,
request_type TEXT NOT NULL,
status TEXT NOT NULL DEFAULT 'queued',
priority INTEGER DEFAULT 0,
data TEXT NOT NULL,
result TEXT,
error_message TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
started_at TIMESTAMP,
completed_at TIMESTAMP,
estimated_time INTEGER,
estimated_tokens INTEGER DEFAULT 0,
used_tokens INTEGER DEFAULT 0,
FOREIGN KEY (user_id) REFERENCES users (id)
)
''')
# Insert default configurations if not exist
defaults = {
......@@ -130,17 +251,28 @@ def init_db(conn: sqlite3.Connection) -> None:
}
for key, value in defaults.items():
cursor.execute('INSERT OR IGNORE INTO config (key, value) VALUES (?, ?)', (key, value))
if config['type'] == 'mysql':
cursor.execute('INSERT IGNORE INTO config (`key`, value) VALUES (?, ?)', (key, value))
else:
cursor.execute('INSERT OR IGNORE INTO config (key, value) VALUES (?, ?)', (key, value))
# Insert default system prompt if not exist
cursor.execute('INSERT OR IGNORE INTO system_prompts (name, content) VALUES (?, ?)',
('default', 'when the action done by the person or persons in the frame changes, or where the scenario change, or where there an active action after a long time of no actions happening'))
if config['type'] == 'mysql':
cursor.execute('INSERT IGNORE INTO system_prompts (name, content) VALUES (?, ?)',
('default', 'when the action done by the person or persons in the frame changes, or where the scenario change, or where there an active action after a long time of no actions happening'))
else:
cursor.execute('INSERT OR IGNORE INTO system_prompts (name, content) VALUES (?, ?)',
('default', 'when the action done by the person or persons in the frame changes, or where the scenario change, or where there an active action after a long time of no actions happening'))
# Insert default admin user if not exist
import hashlib
default_password = hashlib.sha256('admin'.encode()).hexdigest()
cursor.execute('INSERT OR IGNORE INTO users (username, password_hash, role) VALUES (?, ?, ?)',
('admin', default_password, 'admin'))
if config['type'] == 'mysql':
cursor.execute('INSERT IGNORE INTO users (username, password_hash, role, active, email_confirmed) VALUES (?, ?, ?, 1, 1)',
('admin', default_password, 'admin'))
else:
cursor.execute('INSERT OR IGNORE INTO users (username, password_hash, role, active, email_confirmed) VALUES (?, ?, ?, 1, 1)',
('admin', default_password, 'admin'))
conn.commit()
......@@ -159,7 +291,13 @@ def set_config(key: str, value: str) -> None:
"""Set configuration value."""
conn = get_db_connection()
cursor = conn.cursor()
cursor.execute('INSERT OR REPLACE INTO config (key, value) VALUES (?, ?)', (key, value))
config = get_db_config()
if config['type'] == 'mysql':
cursor.execute('INSERT INTO config (`key`, value) VALUES (?, ?) ON DUPLICATE KEY UPDATE value = VALUES(value)', (key, value))
else:
cursor.execute('INSERT OR REPLACE INTO config (key, value) VALUES (?, ?)', (key, value))
conn.commit()
conn.close()
......@@ -188,10 +326,20 @@ def set_system_prompt(name: str, content: str) -> None:
"""Set system prompt."""
conn = get_db_connection()
cursor = conn.cursor()
cursor.execute('''
INSERT OR REPLACE INTO system_prompts (name, content, updated_at)
VALUES (?, ?, CURRENT_TIMESTAMP)
''', (name, content))
config = get_db_config()
if config['type'] == 'mysql':
cursor.execute('''
INSERT INTO system_prompts (name, content, updated_at)
VALUES (?, ?, CURRENT_TIMESTAMP)
ON DUPLICATE KEY UPDATE content = VALUES(content), updated_at = CURRENT_TIMESTAMP
''', (name, content))
else:
cursor.execute('''
INSERT OR REPLACE INTO system_prompts (name, content, updated_at)
VALUES (?, ?, CURRENT_TIMESTAMP)
''', (name, content))
conn.commit()
conn.close()
......@@ -224,13 +372,89 @@ def create_user(username: str, password: str, email: str = None, role: str = 'us
conn.close()
def register_user(username: str, password: str, email: str) -> tuple[bool, str]:
"""Register a new user with email confirmation."""
import hashlib
import secrets
import time
password_hash = hashlib.sha256(password.encode()).hexdigest()
confirmation_token = secrets.token_hex(32)
expires_at = int(time.time()) + (24 * 60 * 60) # 24 hours
conn = get_db_connection()
cursor = conn.cursor()
try:
cursor.execute('''
INSERT INTO users (username, password_hash, email, email_confirmation_token, email_confirmation_expires)
VALUES (?, ?, ?, ?, ?)
''', (username, password_hash, email, confirmation_token, expires_at))
conn.commit()
return True, confirmation_token
except sqlite3.IntegrityError as e:
if 'username' in str(e):
return False, "Username already exists"
elif 'email' in str(e):
return False, "Email already registered"
return False, "Registration failed"
finally:
conn.close()
def confirm_email(token: str) -> bool:
"""Confirm user email with token."""
import time
conn = get_db_connection()
cursor = conn.cursor()
cursor.execute('''
UPDATE users
SET active = 1, email_confirmed = 1, email_confirmation_token = NULL, email_confirmation_expires = NULL
WHERE email_confirmation_token = ? AND email_confirmation_expires > ?
''', (token, int(time.time())))
conn.commit()
success = cursor.rowcount > 0
conn.close()
return success
def get_user_tokens(user_id: int) -> int:
"""Get user's current token balance."""
conn = get_db_connection()
cursor = conn.cursor()
cursor.execute('SELECT tokens FROM users WHERE id = ?', (user_id,))
row = cursor.fetchone()
conn.close()
return row['tokens'] if row else 0
def update_user_tokens(user_id: int, token_change: int) -> bool:
"""Update user's token balance."""
conn = get_db_connection()
cursor = conn.cursor()
cursor.execute('UPDATE users SET tokens = tokens + ? WHERE id = ?', (token_change, user_id))
conn.commit()
success = cursor.rowcount > 0
conn.close()
return success
def get_default_user_tokens() -> int:
"""Get default token allocation for new users."""
return int(get_config('default_user_tokens', '100'))
def get_token_price() -> float:
"""Get price per token in USD."""
return float(get_config('token_price_usd', '0.10'))
def authenticate_user(username: str, password: str) -> Optional[Dict[str, Any]]:
"""Authenticate user and return user info."""
import hashlib
password_hash = hashlib.sha256(password.encode()).hexdigest()
conn = get_db_connection()
cursor = conn.cursor()
cursor.execute('SELECT id, username, email, role, active FROM users WHERE username = ? AND password_hash = ?',
cursor.execute('SELECT id, username, email, role, active, email_confirmed, tokens FROM users WHERE username = ? AND password_hash = ? AND active = 1 AND email_confirmed = 1',
(username, password_hash))
row = cursor.fetchone()
if row:
......
# Video AI Email Module
# Copyright (C) 2024 Stefy Lanza <stefy@sexhack.me>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
Email sending functionality for Video AI.
Supports SMTP and simple email sending.
"""
import smtplib
import ssl
import time
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from typing import Optional
from .config import get_config
def get_smtp_config() -> dict:
"""Get SMTP configuration."""
return {
'server': get_config('smtp_server', 'smtp.gmail.com'),
'port': int(get_config('smtp_port', '587')),
'username': get_config('smtp_username', ''),
'password': get_config('smtp_password', ''),
'use_tls': get_config('smtp_use_tls', 'true').lower() == 'true',
'use_ssl': get_config('smtp_use_ssl', 'false').lower() == 'true'
}
def send_email(to_email: str, subject: str, html_content: str, text_content: str = None) -> bool:
"""Send an email."""
config = get_smtp_config()
if not config['username'] or not config['password']:
print("SMTP not configured. Email sending disabled.")
return False
try:
# Create message
message = MIMEMultipart("alternative")
message["Subject"] = subject
message["From"] = config['username']
message["To"] = to_email
# Add text content
if text_content:
message.attach(MIMEText(text_content, "plain"))
# Add HTML content
message.attach(MIMEText(html_content, "html"))
# Create SMTP connection
if config['use_ssl']:
server = smtplib.SMTP_SSL(config['server'], config['port'])
else:
server = smtplib.SMTP(config['server'], config['port'])
if config['use_tls']:
server.starttls()
# Login and send
server.login(config['username'], config['password'])
server.sendmail(config['username'], to_email, message.as_string())
server.quit()
return True
except Exception as e:
print(f"Email sending failed: {e}")
return False
def send_registration_confirmation(email: str, username: str, confirmation_token: str) -> bool:
"""Send email confirmation for user registration."""
base_url = get_config('base_url', 'http://localhost:5000')
confirmation_url = f"{base_url}/confirm/{confirmation_token}"
subject = "Welcome to Video AI - Please Confirm Your Email"
html_content = f"""
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Email Confirmation</title>
<style>
body {{ font-family: Arial, sans-serif; background-color: #f4f4f4; margin: 0; padding: 20px; }}
.container {{ max-width: 600px; margin: auto; background: white; padding: 30px; border-radius: 10px; box-shadow: 0 0 10px rgba(0,0,0,0.1); }}
h1 {{ color: #333; text-align: center; }}
.btn {{ display: inline-block; padding: 12px 24px; background: #007bff; color: white; text-decoration: none; border-radius: 5px; margin: 20px 0; }}
.footer {{ margin-top: 30px; padding-top: 20px; border-top: 1px solid #eee; color: #666; font-size: 12px; }}
</style>
</head>
<body>
<div class="container">
<h1>Welcome to Video AI!</h1>
<p>Hello {username},</p>
<p>Thank you for registering with Video AI. To complete your registration and start using our professional video analysis tools, please confirm your email address by clicking the button below:</p>
<div style="text-align: center;">
<a href="{confirmation_url}" class="btn">Confirm Email Address</a>
</div>
<p>If the button doesn't work, you can also copy and paste this link into your browser:</p>
<p><a href="{confirmation_url}">{confirmation_url}</a></p>
<p>This link will expire in 24 hours for security reasons.</p>
<div class="footer">
<p>If you didn't create an account with Video AI, please ignore this email.</p>
<p>&copy; 2024 Video AI. All rights reserved.</p>
</div>
</div>
</body>
</html>
"""
text_content = f"""
Welcome to Video AI!
Hello {username},
Thank you for registering with Video AI. To complete your registration, please confirm your email address by visiting:
{confirmation_url}
This link will expire in 24 hours.
If you didn't create an account, please ignore this email.
Best regards,
Video AI Team
"""
return send_email(email, subject, html_content, text_content)
def send_password_reset(email: str, username: str, reset_token: str) -> bool:
"""Send password reset email."""
base_url = get_config('base_url', 'http://localhost:5000')
reset_url = f"{base_url}/reset-password/{reset_token}"
subject = "Video AI - Password Reset Request"
html_content = f"""
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Password Reset</title>
<style>
body {{ font-family: Arial, sans-serif; background-color: #f4f4f4; margin: 0; padding: 20px; }}
.container {{ max-width: 600px; margin: auto; background: white; padding: 30px; border-radius: 10px; box-shadow: 0 0 10px rgba(0,0,0,0.1); }}
h1 {{ color: #333; text-align: center; }}
.btn {{ display: inline-block; padding: 12px 24px; background: #dc3545; color: white; text-decoration: none; border-radius: 5px; margin: 20px 0; }}
.footer {{ margin-top: 30px; padding-top: 20px; border-top: 1px solid #eee; color: #666; font-size: 12px; }}
</style>
</head>
<body>
<div class="container">
<h1>Password Reset Request</h1>
<p>Hello {username},</p>
<p>We received a request to reset your password for your Video AI account. Click the button below to reset your password:</p>
<div style="text-align: center;">
<a href="{reset_url}" class="btn">Reset Password</a>
</div>
<p>If you didn't request a password reset, please ignore this email. Your password will remain unchanged.</p>
<p>This link will expire in 1 hour for security reasons.</p>
<div class="footer">
<p>&copy; 2024 Video AI. All rights reserved.</p>
</div>
</div>
</body>
</html>
"""
text_content = f"""
Password Reset Request
Hello {username},
We received a request to reset your password. Visit this link to reset it:
{reset_url}
This link expires in 1 hour. If you didn't request this, ignore this email.
Best regards,
Video AI Team
"""
return send_email(email, subject, html_content, text_content)
def send_payment_confirmation(email: str, username: str, tokens_purchased: int, amount_paid: float, payment_method: str) -> bool:
"""Send payment confirmation email."""
subject = f"Video AI - Payment Confirmation ({tokens_purchased} tokens)"
html_content = f"""
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Payment Confirmation</title>
<style>
body {{ font-family: Arial, sans-serif; background-color: #f4f4f4; margin: 0; padding: 20px; }}
.container {{ max-width: 600px; margin: auto; background: white; padding: 30px; border-radius: 10px; box-shadow: 0 0 10px rgba(0,0,0,0.1); }}
h1 {{ color: #333; text-align: center; }}
.success {{ background: #d4edda; color: #155724; padding: 15px; border-radius: 5px; margin: 20px 0; }}
.details {{ background: #f8f9fa; padding: 15px; border-radius: 5px; margin: 20px 0; }}
.footer {{ margin-top: 30px; padding-top: 20px; border-top: 1px solid #eee; color: #666; font-size: 12px; }}
</style>
</head>
<body>
<div class="container">
<h1>Payment Successful!</h1>
<div class="success">
<strong>Thank you for your purchase, {username}!</strong>
</div>
<div class="details">
<h3>Transaction Details:</h3>
<p><strong>Tokens Purchased:</strong> {tokens_purchased}</p>
<p><strong>Amount Paid:</strong> ${amount_paid:.2f}</p>
<p><strong>Payment Method:</strong> {payment_method}</p>
<p><strong>Transaction Date:</strong> {time.strftime('%Y-%m-%d %H:%M:%S')}</p>
</div>
<p>Your tokens have been added to your account and are ready to use for video analysis jobs.</p>
<div class="footer">
<p>If you have any questions about your purchase, please contact our support team.</p>
<p>&copy; 2024 Video AI. All rights reserved.</p>
</div>
</div>
</body>
</html>
"""
text_content = f"""
Payment Confirmation - Video AI
Thank you for your purchase, {username}!
Transaction Details:
- Tokens Purchased: {tokens_purchased}
- Amount Paid: ${amount_paid:.2f}
- Payment Method: {payment_method}
- Date: {time.strftime('%Y-%m-%d %H:%M:%S')}
Your tokens have been added to your account.
Best regards,
Video AI Team
"""
return send_email(email, subject, html_content, text_content)
\ No newline at end of file
# Video AI Payment Processing Module
# Copyright (C) 2024 Stefy Lanza <stefy@sexhack.me>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
Payment processing for Video AI token purchases.
Supports Stripe (credit cards), PayPal, and cryptocurrency payments.
"""
import time
import json
from typing import Dict, Any, Optional, Tuple, List
from .config import get_config
from .database import update_user_tokens
from .email import send_payment_confirmation
class PaymentProcessor:
"""Base class for payment processors."""
def __init__(self):
self.name = "Base"
self.enabled = False
def process_payment(self, user_id: int, tokens: int, amount: float, currency: str = 'USD') -> Tuple[bool, str, Optional[str]]:
"""Process a payment. Returns (success, message, transaction_id)"""
raise NotImplementedError
def create_payment_intent(self, tokens: int, amount: float) -> Optional[Dict[str, Any]]:
"""Create a payment intent for frontend integration."""
return None
class StripeProcessor(PaymentProcessor):
"""Stripe payment processor for credit cards."""
def __init__(self):
super().__init__()
self.name = "Stripe"
self.secret_key = get_config('stripe_secret_key', '')
self.publishable_key = get_config('stripe_publishable_key', '')
self.enabled = bool(self.secret_key and self.publishable_key)
if self.enabled:
try:
import stripe
stripe.api_key = self.secret_key
except ImportError:
self.enabled = False
print("Stripe not available. Install with: pip install stripe")
def process_payment(self, user_id: int, tokens: int, amount: float, currency: str = 'USD') -> Tuple[bool, str, Optional[str]]:
"""Process Stripe payment."""
if not self.enabled:
return False, "Stripe payment processing not configured", None
try:
import stripe
# Create payment intent
intent = stripe.PaymentIntent.create(
amount=int(amount * 100), # Convert to cents
currency=currency.lower(),
metadata={
'user_id': str(user_id),
'tokens': str(tokens)
}
)
return True, "Payment intent created", intent.id
except Exception as e:
return False, f"Payment processing failed: {str(e)}", None
def create_payment_intent(self, tokens: int, amount: float) -> Optional[Dict[str, Any]]:
"""Create Stripe payment intent for frontend."""
if not self.enabled:
return None
try:
import stripe
intent = stripe.PaymentIntent.create(
amount=int(amount * 100),
currency='usd',
metadata={'tokens': str(tokens)}
)
return {
'client_secret': intent.client_secret,
'amount': amount,
'tokens': tokens
}
except Exception as e:
print(f"Stripe payment intent creation failed: {e}")
return None
class PayPalProcessor(PaymentProcessor):
"""PayPal payment processor."""
def __init__(self):
super().__init__()
self.name = "PayPal"
self.client_id = get_config('paypal_client_id', '')
self.client_secret = get_config('paypal_client_secret', '')
self.enabled = bool(self.client_id and self.client_secret)
if self.enabled:
try:
# PayPal SDK would go here
pass
except ImportError:
self.enabled = False
def process_payment(self, user_id: int, tokens: int, amount: float, currency: str = 'USD') -> Tuple[bool, str, Optional[str]]:
"""Process PayPal payment."""
if not self.enabled:
return False, "PayPal payment processing not configured", None
# PayPal integration would go here
# For now, return placeholder
return False, "PayPal integration coming soon", None
class CryptoProcessor(PaymentProcessor):
"""Cryptocurrency payment processor."""
def __init__(self):
super().__init__()
self.name = "Cryptocurrency"
self.btc_address = get_config('btc_payment_address', '')
self.eth_address = get_config('eth_payment_address', '')
self.enabled = bool(self.btc_address or self.eth_address)
def process_payment(self, user_id: int, tokens: int, amount: float, currency: str = 'USD') -> Tuple[bool, str, Optional[str]]:
"""Process cryptocurrency payment."""
if not self.enabled:
return False, "Cryptocurrency payments not configured", None
# Generate payment addresses and instructions
transaction_id = f"crypto_{user_id}_{int(time.time())}"
payment_info = {
'transaction_id': transaction_id,
'amount_usd': amount,
'tokens': tokens,
'btc_address': self.btc_address,
'eth_address': self.eth_address,
'status': 'pending'
}
# In a real implementation, you'd store this in database
# and provide webhook endpoint for payment confirmation
return True, "Cryptocurrency payment initiated. Please send payment to provided address.", transaction_id
# Global payment processors
stripe_processor = StripeProcessor()
paypal_processor = PayPalProcessor()
crypto_processor = CryptoProcessor()
def get_available_processors() -> Dict[str, PaymentProcessor]:
"""Get all available payment processors."""
return {
'stripe': stripe_processor,
'paypal': paypal_processor,
'crypto': crypto_processor
}
def process_payment(user_id: int, processor_name: str, tokens: int, amount: float) -> Tuple[bool, str, Optional[str]]:
"""Process payment using specified processor."""
processors = get_available_processors()
if processor_name not in processors:
return False, "Payment processor not found", None
processor = processors[processor_name]
success, message, transaction_id = processor.process_payment(user_id, tokens, amount)
if success and transaction_id:
# Add tokens to user account
if update_user_tokens(user_id, tokens):
# Send confirmation email
from .database import get_user_by_id
user = get_user_by_id(user_id)
if user and user.get('email'):
send_payment_confirmation(user['email'], user['username'], tokens, amount, processor.name)
return True, f"Payment successful! {tokens} tokens added to your account.", transaction_id
else:
return False, "Payment processed but token update failed", transaction_id
return success, message, transaction_id
def create_payment_intent(processor_name: str, tokens: int, amount: float) -> Optional[Dict[str, Any]]:
"""Create payment intent for frontend integration."""
processors = get_available_processors()
if processor_name not in processors:
return None
processor = processors[processor_name]
return processor.create_payment_intent(tokens, amount)
def get_token_packages() -> List[Dict[str, Any]]:
"""Get available token packages."""
return [
{'tokens': 100, 'price': 10.00, 'popular': False},
{'tokens': 250, 'price': 22.50, 'popular': False},
{'tokens': 500, 'price': 40.00, 'popular': True},
{'tokens': 1000, 'price': 70.00, 'popular': False},
{'tokens': 2500, 'price': 150.00, 'popular': False},
]
def calculate_price(tokens: int) -> float:
"""Calculate price for given number of tokens."""
from .database import get_token_price
return tokens * get_token_price()
\ No newline at end of file
......@@ -32,7 +32,10 @@ from .config import (
set_analysis_backend, set_training_backend, set_default_model, set_frame_interval,
get_comm_type, set_comm_type, set_max_concurrent_jobs
)
from .auth import login_user, logout_user, get_current_user, require_auth, require_admin, api_authenticate
from .auth import login_user, logout_user, get_current_user, require_auth, require_admin, api_authenticate, register_user, confirm_email
from .email import send_registration_confirmation
from .database import get_default_user_tokens, update_user_tokens, get_user_tokens, get_token_price
from .payments import get_token_packages, calculate_price, process_payment, create_payment_intent, get_available_processors
from .database import (
create_user, get_all_users, update_user_role, delete_user,
create_api_token, get_user_api_tokens, revoke_api_token
......@@ -153,6 +156,125 @@ def login():
'''
return render_template_string(html, error=error)
@app.route('/register', methods=['GET', 'POST'])
def register():
error = None
success = None
if request.method == 'POST':
username = request.form.get('username')
password = request.form.get('password')
confirm_password = request.form.get('confirm_password')
email = request.form.get('email')
if not all([username, password, confirm_password, email]):
error = "All fields are required"
elif password != confirm_password:
error = "Passwords do not match"
elif len(password) < 8:
error = "Password must be at least 8 characters long"
else:
# Register user
success_reg, message = register_user(username, password, email)
if success_reg:
# Send confirmation email
if send_registration_confirmation(email, username, message):
success = "Registration successful! Please check your email to confirm your account."
else:
success = "Registration successful, but email confirmation could not be sent. Please contact support."
else:
error = message
html = '''
<!DOCTYPE html>
<html>
<head>
<title>Register - Video AI</title>
<style>
body { font-family: Arial, sans-serif; background: #f4f4f4; margin: 0; padding: 20px; display: flex; justify-content: center; align-items: center; min-height: 100vh; }
.register-form { background: white; padding: 40px; border-radius: 8px; box-shadow: 0 0 10px rgba(0,0,0,0.1); width: 400px; }
h1 { text-align: center; color: #333; margin-bottom: 30px; }
form { display: flex; flex-direction: column; }
label { margin-bottom: 5px; font-weight: bold; }
input { padding: 10px; margin-bottom: 15px; border: 1px solid #ddd; border-radius: 4px; }
button { background: #28a745; color: white; padding: 12px; border: none; border-radius: 4px; cursor: pointer; font-size: 16px; }
button:hover { background: #218838; }
.error { color: red; margin-bottom: 15px; text-align: center; }
.success { color: green; margin-bottom: 15px; text-align: center; }
.login-link { text-align: center; margin-top: 20px; }
.login-link a { color: #007bff; text-decoration: none; }
</style>
</head>
<body>
<div class="register-form">
<h1>Create Account</h1>
{% if error %}
<div class="error">{{ error }}</div>
{% endif %}
{% if success %}
<div class="success">{{ success }}</div>
{% endif %}
<form method="post">
<label>Username:</label>
<input type="text" name="username" required>
<label>Email:</label>
<input type="email" name="email" required>
<label>Password:</label>
<input type="password" name="password" required>
<label>Confirm Password:</label>
<input type="password" name="confirm_password" required>
<button type="submit">Register</button>
</form>
<div class="login-link">
<a href="/login">Already have an account? Login</a>
</div>
</div>
</body>
</html>
'''
return render_template_string(html, error=error, success=success)
@app.route('/confirm/<token>')
def confirm_email_route(token):
if confirm_email(token):
message = "Email confirmed successfully! You can now login."
success = True
else:
message = "Invalid or expired confirmation link."
success = False
html = f'''
<!DOCTYPE html>
<html>
<head>
<title>Email Confirmation - Video AI</title>
<style>
body {{ font-family: Arial, sans-serif; background: #f4f4f4; margin: 0; padding: 20px; display: flex; justify-content: center; align-items: center; min-height: 100vh; }}
.confirm-box {{ background: white; padding: 40px; border-radius: 8px; box-shadow: 0 0 10px rgba(0,0,0,0.1); width: 400px; text-align: center; }}
h1 {{ color: #333; margin-bottom: 20px; }}
.message {{ margin: 20px 0; padding: 15px; border-radius: 4px; }}
.success {{ background: #d4edda; color: #155724; }}
.error {{ background: #f8d7da; color: #721c24; }}
a {{ display: inline-block; margin-top: 20px; padding: 10px 20px; background: #007bff; color: white; text-decoration: none; border-radius: 4px; }}
</style>
</head>
<body>
<div class="confirm-box">
<h1>Email Confirmation</h1>
<div class="message {'success' if success else 'error'}">
{message}
</div>
<a href="/login">Continue to Login</a>
</div>
</body>
</html>
'''
return html
@app.route('/logout')
def logout():
session_id = get_session_id()
......@@ -163,6 +285,17 @@ def logout():
return resp
@app.route('/')
def index():
session_id = get_session_id()
user = get_current_user(session_id) if session_id else None
if user:
# Show dashboard for logged-in users
return dashboard()
else:
# Show landing page for non-logged-in users
return render_template_string(open('templates/landing.html').read())
@require_login()
def dashboard():
session_id = get_session_id()
......@@ -619,6 +752,60 @@ def admin_config():
if gpu_type:
set_runpod_gpu_type(gpu_type)
# Registration and token configuration
from .config import set_allow_registration, set_default_user_tokens, set_token_price, set_base_url
set_allow_registration(request.form.get('allow_registration') == 'true')
set_default_user_tokens(int(request.form.get('default_user_tokens', '100')))
set_token_price(float(request.form.get('token_price_usd', '0.10')))
set_base_url(request.form.get('base_url', 'http://localhost:5000'))
# Email configuration
from .config import set_smtp_server, set_smtp_port, set_smtp_credentials, set_smtp_security
set_smtp_server(request.form.get('smtp_server', 'smtp.gmail.com'))
set_smtp_port(int(request.form.get('smtp_port', '587')))
smtp_username = request.form.get('smtp_username')
smtp_password = request.form.get('smtp_password')
if smtp_username and smtp_password:
set_smtp_credentials(smtp_username, smtp_password)
set_smtp_security(
request.form.get('smtp_use_tls') == 'true',
request.form.get('smtp_use_ssl') == 'true'
)
# Payment configuration
from .config import set_stripe_keys, set_paypal_credentials, set_crypto_addresses
stripe_pub = request.form.get('stripe_publishable_key')
stripe_sec = request.form.get('stripe_secret_key')
if stripe_pub and stripe_sec:
set_stripe_keys(stripe_pub, stripe_sec)
paypal_id = request.form.get('paypal_client_id')
paypal_secret = request.form.get('paypal_client_secret')
if paypal_id and paypal_secret:
set_paypal_credentials(paypal_id, paypal_secret)
btc_addr = request.form.get('btc_payment_address')
eth_addr = request.form.get('eth_payment_address')
if btc_addr or eth_addr:
set_crypto_addresses(btc_addr or '', eth_addr or '')
# Database configuration
from .config import set_db_type, set_db_sqlite_path, set_db_mysql_config
db_type = request.form.get('db_type', 'sqlite')
set_db_type(db_type)
if db_type == 'sqlite':
set_db_sqlite_path(request.form.get('db_sqlite_path', 'vidai.db'))
elif db_type == 'mysql':
set_db_mysql_config(
host=request.form.get('db_mysql_host', 'localhost'),
port=int(request.form.get('db_mysql_port', '3306')),
user=request.form.get('db_mysql_user', 'vidai'),
password=request.form.get('db_mysql_password', ''),
database=request.form.get('db_mysql_database', 'vidai'),
charset=request.form.get('db_mysql_charset', 'utf8mb4')
)
settings = get_all_settings()
html = '''
<!DOCTYPE html>
......@@ -719,6 +906,130 @@ def admin_config():
<button type="submit">Save RunPod Configuration</button>
</form>
</div>
<div class="section">
<h3>User Registration & Tokens</h3>
<form method="post">
<label>
<input type="checkbox" name="allow_registration" value="true" {% if settings.get('allow_registration', True) %}checked{% endif %}>
<span class="checkbox-label">Allow user registration</span>
</label>
<label>Default Tokens per User:</label>
<input type="number" name="default_user_tokens" value="{{ settings.get('default_user_tokens', 100) }}" min="0">
<label>Token Price (USD per token):</label>
<input type="number" name="token_price_usd" value="{{ settings.get('token_price_usd', 0.10) }}" step="0.01" min="0">
<label>Base URL:</label>
<input type="url" name="base_url" value="{{ settings.get('base_url', 'http://localhost:5000') }}" placeholder="http://your-domain.com">
<button type="submit">Save Registration Settings</button>
</form>
</div>
<div class="section">
<h3>Email Configuration</h3>
<form method="post">
<label>SMTP Server:</label>
<input type="text" name="smtp_server" value="{{ settings.get('smtp_server', 'smtp.gmail.com') }}">
<label>SMTP Port:</label>
<input type="number" name="smtp_port" value="{{ settings.get('smtp_port', 587) }}" min="1" max="65535">
<label>SMTP Username:</label>
<input type="text" name="smtp_username" value="{{ settings.get('smtp_username', '') }}">
<label>SMTP Password:</label>
<input type="password" name="smtp_password" placeholder="Enter SMTP password">
<label>
<input type="checkbox" name="smtp_use_tls" value="true" {% if settings.get('smtp_use_tls', True) %}checked{% endif %}>
<span class="checkbox-label">Use TLS</span>
</label>
<label>
<input type="checkbox" name="smtp_use_ssl" value="true" {% if settings.get('smtp_use_ssl', False) %}checked{% endif %}>
<span class="checkbox-label">Use SSL</span>
</label>
<button type="submit">Save Email Configuration</button>
</form>
</div>
<div class="section">
<h3>Payment Configuration</h3>
<form method="post">
<h4>Stripe (Credit Cards)</h4>
<label>Publishable Key:</label>
<input type="text" name="stripe_publishable_key" value="{{ settings.get('stripe_publishable_key', '') }}">
<label>Secret Key:</label>
<input type="password" name="stripe_secret_key" placeholder="Enter Stripe secret key">
<h4>PayPal</h4>
<label>Client ID:</label>
<input type="text" name="paypal_client_id" value="{{ settings.get('paypal_client_id', '') }}">
<label>Client Secret:</label>
<input type="password" name="paypal_client_secret" placeholder="Enter PayPal client secret">
<h4>Cryptocurrency</h4>
<label>Bitcoin Address:</label>
<input type="text" name="btc_payment_address" value="{{ settings.get('btc_payment_address', '') }}" placeholder="Enter BTC address">
<label>Ethereum Address:</label>
<input type="text" name="eth_payment_address" value="{{ settings.get('eth_payment_address', '') }}" placeholder="Enter ETH address">
<button type="submit">Save Payment Configuration</button>
</form>
</div>
<div class="section">
<h3>Database Configuration</h3>
<form method="post">
<label>Database Type:</label>
<select name="db_type">
<option value="sqlite" {% if settings.get('db_type', 'sqlite') == 'sqlite' %}selected{% endif %}>SQLite</option>
<option value="mysql" {% if settings.get('db_type', 'sqlite') == 'mysql' %}selected{% endif %}>MySQL</option>
</select>
<div id="sqlite_settings" style="{% if settings.get('db_type', 'sqlite') != 'sqlite' %}display: none;{% endif %}">
<label>SQLite Database Path:</label>
<input type="text" name="db_sqlite_path" value="{{ settings.get('db_sqlite_path', 'vidai.db') }}">
</div>
<div id="mysql_settings" style="{% if settings.get('db_type', 'sqlite') != 'mysql' %}display: none;{% endif %}">
<label>MySQL Host:</label>
<input type="text" name="db_mysql_host" value="{{ settings.get('db_mysql_host', 'localhost') }}">
<label>MySQL Port:</label>
<input type="number" name="db_mysql_port" value="{{ settings.get('db_mysql_port', 3306) }}" min="1" max="65535">
<label>MySQL Username:</label>
<input type="text" name="db_mysql_user" value="{{ settings.get('db_mysql_user', 'vidai') }}">
<label>MySQL Password:</label>
<input type="password" name="db_mysql_password" placeholder="Enter MySQL password">
<label>MySQL Database:</label>
<input type="text" name="db_mysql_database" value="{{ settings.get('db_mysql_database', 'vidai') }}">
<label>MySQL Charset:</label>
<input type="text" name="db_mysql_charset" value="{{ settings.get('db_mysql_charset', 'utf8mb4') }}">
</div>
<button type="submit">Save Database Configuration</button>
</form>
<script>
document.querySelector('select[name="db_type"]').addEventListener('change', function() {
const dbType = this.value;
document.getElementById('sqlite_settings').style.display = dbType === 'sqlite' ? 'block' : 'none';
document.getElementById('mysql_settings').style.display = dbType === 'mysql' ? 'block' : 'none';
});
</script>
</div>
</div>
</body>
</html>
......@@ -994,6 +1305,148 @@ def api_docs():
'''
return render_template_string(html)
@app.route('/tokens', methods=['GET', 'POST'])
@require_login()
def tokens():
session_id = get_session_id()
user = get_current_user(session_id)
user_tokens = get_user_tokens(user['id'])
token_packages = get_token_packages()
processors = get_available_processors()
message = None
error = None
if request.method == 'POST':
action = request.form.get('action')
if action == 'purchase':
package_tokens = int(request.form.get('tokens', 0))
processor_name = request.form.get('processor', 'stripe')
if package_tokens > 0:
amount = calculate_price(package_tokens)
success, msg, transaction_id = process_payment(user['id'], processor_name, package_tokens, amount)
if success:
message = msg
user_tokens = get_user_tokens(user['id']) # Refresh token count
else:
error = msg
html = f'''
<!DOCTYPE html>
<html>
<head>
<title>Token Management - Video AI</title>
<style>
body {{ font-family: Arial, sans-serif; background: #f4f4f4; margin: 0; padding: 20px; }}
.container {{ max-width: 1000px; margin: auto; }}
.header {{ background: white; padding: 20px; border-radius: 8px; box-shadow: 0 0 10px rgba(0,0,0,0.1); margin-bottom: 20px; display: flex; justify-content: space-between; align-items: center; }}
.nav {{ display: flex; gap: 20px; }}
.nav a {{ text-decoration: none; color: #007bff; }}
.token-balance {{ background: #28a745; color: white; padding: 10px 20px; border-radius: 20px; font-weight: bold; }}
.content {{ display: grid; grid-template-columns: 1fr 300px; gap: 20px; }}
.main {{ background: white; padding: 20px; border-radius: 8px; box-shadow: 0 0 10px rgba(0,0,0,0.1); }}
.sidebar {{ background: white; padding: 20px; border-radius: 8px; box-shadow: 0 0 10px rgba(0,0,0,0.1); }}
.packages {{ display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 15px; margin: 20px 0; }}
.package {{ border: 2px solid #e0e0e0; padding: 20px; border-radius: 8px; text-align: center; transition: all 0.3s ease; }}
.package:hover {{ border-color: #007bff; transform: translateY(-2px); }}
.package.popular {{ border-color: #28a745; position: relative; }}
.package.popular::before {{ content: 'Most Popular'; position: absolute; top: -10px; left: 50%; transform: translateX(-50%); background: #28a745; color: white; padding: 3px 10px; border-radius: 10px; font-size: 0.8rem; }}
.package h3 {{ margin: 10px 0; }}
.price {{ font-size: 1.5rem; font-weight: bold; color: #007bff; margin: 10px 0; }}
.btn {{ display: inline-block; padding: 8px 16px; background: #007bff; color: white; text-decoration: none; border-radius: 4px; margin: 5px; }}
.btn:hover {{ background: #0056b3; }}
.message {{ padding: 10px; border-radius: 4px; margin: 10px 0; }}
.success {{ background: #d4edda; color: #155724; }}
.error {{ background: #f8d7da; color: #721c24; }}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>Token Management</h1>
<div class="nav">
<a href="/">Dashboard</a> |
<a href="/analyze">Analyze</a> |
<a href="/logout">Logout</a>
</div>
</div>
<div class="token-balance">
Current Balance: {user_tokens} tokens
</div>
{% if message %}
<div class="message success">{message}</div>
{% endif %}
{% if error %}
<div class="message error">{error}</div>
{% endif %}
<div class="content">
<div class="main">
<h2>Purchase Tokens</h2>
<div class="packages">
{% for package in token_packages %}
<div class="package {% if package.popular %}popular{% endif %}">
<h3>{package.tokens} Tokens</h3>
<div class="price">${package.price}</div>
<form method="post" style="display: inline;">
<input type="hidden" name="action" value="purchase">
<input type="hidden" name="tokens" value="{package.tokens}">
<select name="processor" style="margin: 5px 0;">
{% for name, proc in processors.items() %}
{% if proc.enabled %}
<option value="{name}">{proc.name}</option>
{% endif %}
{% endfor %}
</select><br>
<button type="submit" class="btn">Purchase</button>
</form>
</div>
{% endfor %}
</div>
<h3>Custom Amount</h3>
<form method="post">
<input type="hidden" name="action" value="purchase">
<input type="number" name="tokens" placeholder="Number of tokens" min="10" required>
<select name="processor">
{% for name, proc in processors.items() %}
{% if proc.enabled %}
<option value="{name}">{proc.name}</option>
{% endif %}
{% endfor %}
</select>
<button type="submit" class="btn">Purchase Custom Amount</button>
</form>
</div>
<div class="sidebar">
<h3>Token Usage</h3>
<p>Tokens are used for video analysis jobs:</p>
<ul>
<li>~10 tokens per minute of video</li>
<li>~50 tokens per image analysis</li>
<li>~200 tokens per training job</li>
</ul>
<h3>Payment Methods</h3>
<ul>
<li><strong>Credit Card:</strong> Instant processing via Stripe</li>
<li><strong>PayPal:</strong> Coming soon</li>
<li><strong>Crypto:</strong> BTC/ETH addresses provided</li>
</ul>
</div>
</div>
</div>
</body>
</html>
'''
return render_template_string(html, user=user, user_tokens=user_tokens, token_packages=token_packages, processors=processors, message=message, error=error)
@app.route('/static/<path:filename>')
def serve_static(filename):
return send_from_directory('static', filename)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment