Update project branding and add favicon

- Change project name from 'Video AI Analysis Tool' to 'VidAI'
- Update tagline to 'The adult content friendly video automation system'
- Change all 'Video AI Pro' references to 'VidAI' in templates
- Add favicon using image.jpg in landing page
- Add route to serve image.jpg file
- Update PyInstaller build script to include image.jpg as icon
- Update landing page title and meta description
parent 501bfcca
# Video AI Analysis Tool # VidAI
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. The adult content friendly video automation system. 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 ## Features
......
...@@ -39,7 +39,7 @@ pyinstaller --onedir --name vidai-backend --hidden-import torch --hidden-import ...@@ -39,7 +39,7 @@ pyinstaller --onedir --name vidai-backend --hidden-import torch --hidden-import
REM Build web interface REM Build web interface
echo Building web interface... echo Building web interface...
pyinstaller --onedir --name vidai-web --add-data "templates;templates" --add-data "static;static" --hidden-import flask vidai/web.py pyinstaller --onedir --name vidai-web --icon image.jpg --add-data "templates;templates" --add-data "static;static" --add-data "image.jpg;." --hidden-import flask vidai/web.py
REM Build analysis worker REM Build analysis worker
echo Building analysis worker... echo Building analysis worker...
......
<!DOCTYPE html>
<html>
<head>
<title>Analyze Media - VidAI</title>
<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; background: #f4f4f4; margin: 0; padding: 20px; display: flex; justify-content: center; align-items: flex-start; }
.main { flex: 1; max-width: 800px; background: white; padding: 20px; border-radius: 8px; box-shadow: 0 0 10px rgba(0,0,0,0.1); margin-right: 20px; }
.sidebar { width: 300px; background: white; padding: 20px; border-radius: 8px; box-shadow: 0 0 10px rgba(0,0,0,0.1); }
h1 { color: #333; text-align: center; }
nav { text-align: center; margin-bottom: 20px; }
nav a { margin: 0 10px; text-decoration: none; color: #007bff; }
form { margin-bottom: 20px; }
label { display: block; margin-bottom: 5px; }
input[type="text"], input[type="file"], textarea { width: 100%; padding: 8px; margin-bottom: 10px; border: 1px solid #ccc; border-radius: 4px; }
input[type="submit"] { background: #007bff; color: white; padding: 10px; border: none; border-radius: 4px; cursor: pointer; }
input[type="submit"]:hover { background: #0056b3; }
.result { background: #e9ecef; padding: 10px; border-radius: 4px; }
.stats { font-size: 14px; }
.tokens { background: #f0f9ff; padding: 1rem; border-radius: 8px; margin-bottom: 1rem; border-left: 4px solid #667eea; }
.form-group { margin-bottom: 1.5rem; }
.form-group label { display: block; margin-bottom: 0.5rem; color: #374151; font-weight: 500; }
.btn { padding: 0.75rem 2rem; background: #667eea; color: white; border: none; border-radius: 8px; font-size: 1rem; font-weight: 600; cursor: pointer; }
.btn:hover { background: #5a67d8; }
.alert { padding: 0.75rem; border-radius: 8px; margin-bottom: 1rem; }
.alert-error { background: #fee2e2; color: #dc2626; border: 1px solid #fecaca; }
.alert-success { background: #d1fae5; color: #065f46; border: 1px solid #a7f3d0; }
</style>
<script>
function updateStats() {
fetch('/stats')
.then(response => response.json())
.then(data => {
let html = '<h3>GPU Stats</h3>';
html += `<p style="color: ${data.status === 'Idle' ? 'green' : 'orange'};">Status: ${data.status}</p>`;
if (data.elapsed > 0) {
html += `<p>Elapsed: ${data.elapsed.toFixed(1)}s</p>`;
}
if (data.gpu_count > 0) {
data.gpus.forEach((gpu, i) => {
let memPercent = (gpu.memory_used / gpu.memory_total * 100).toFixed(1);
html += `<p>GPU ${i}: ${gpu.name}<br>Memory: <progress value="${gpu.memory_used}" max="${gpu.memory_total}"></progress> ${gpu.memory_used.toFixed(2)} / ${gpu.memory_total.toFixed(2)} GB (${memPercent}%)<br>Utilization: ${gpu.utilization}%</p>`;
});
} else {
html += '<p>No GPUs detected</p>';
}
html += `<p>CPU: ${data.cpu_percent.toFixed(1)}%</p>`;
html += `<p>RAM: ${data.ram_used.toFixed(2)} / ${data.ram_total.toFixed(2)} GB</p>`;
document.getElementById('stats').innerHTML = html;
if (data.result) {
document.getElementById('result_div').innerHTML = '<h3>Result:</h3><p>' + data.result + '</p>';
document.getElementById('result_div').style.display = 'block';
}
})
.catch(e => {
document.getElementById('stats').innerHTML = '<p>Error loading stats</p>';
});
}
setInterval(updateStats, 5000);
window.onload = updateStats;
</script>
</head>
<body>
<div class="main">
<h1>VidAI</h1>
<nav>
<a href="/dashboard">Dashboard</a> | <a href="/analyze">Analyze</a> | <a href="/train">Train</a> | <a href="/history">History</a> | <a href="/settings">Settings</a>
</nav>
<div class="tokens">
<strong>Available Tokens:</strong> {{ tokens }} (Analysis costs ~10 tokens)
</div>
<h2>Analyze Image/Video</h2>
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
{% for category, message in messages %}
<div class="alert alert-{{ 'error' if category == 'error' else 'success' }}">{{ message }}</div>
{% endfor %}
{% endif %}
{% endwith %}
<form method="post" enctype="multipart/form-data">
<div class="form-group">
<label>Model Path: <input type="text" name="model_path" value="Qwen/Qwen2.5-VL-7B-Instruct"></label>
</div>
<div class="form-group">
<label>Upload File: <input type="file" name="file" accept="image/*,video/*"></label>
</div>
<div class="form-group">
<label>Or Local Path: <input type="text" name="local_path" placeholder="/path/to/your/media/file.mp4"></label>
</div>
<div class="form-group">
<label>Frame Interval (seconds): <input type="number" name="interval" value="10" min="1"></label>
</div>
<div class="form-group">
<label>Prompt: <textarea name="prompt" rows="5" cols="80">Describe this image.</textarea></label>
</div>
<input type="submit" value="Analyze" class="btn">
</form>
<div class="result" id="result_div" style="display:none;"></div>
{% if result %}
<div class="result">
<h3>Result:</h3>
<p>{{ result }}</p>
</div>
{% endif %}
</div>
<div class="sidebar">
<div id="stats" class="stats">Loading stats...</div>
</div>
</body>
</html>
\ No newline at end of file
<!DOCTYPE html>
<html>
<head>
<title>Dashboard - VidAI</title>
<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; background: #f8fafc; }
.header { background: white; padding: 1rem 2rem; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }
.header-content { display: flex; justify-content: space-between; align-items: center; max-width: 1200px; margin: 0 auto; }
.logo { font-size: 1.5rem; font-weight: 700; color: #667eea; }
.user-menu { display: flex; align-items: center; gap: 1rem; }
.container { max-width: 1200px; margin: 2rem auto; padding: 0 2rem; }
.stats-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 1.5rem; margin-bottom: 2rem; }
.stat-card { background: white; padding: 1.5rem; border-radius: 12px; box-shadow: 0 2px 10px rgba(0,0,0,0.05); }
.stat-value { font-size: 2rem; font-weight: 700; color: #667eea; margin-bottom: 0.5rem; }
.stat-label { color: #64748b; font-size: 0.9rem; }
.quick-actions { background: white; padding: 2rem; border-radius: 12px; box-shadow: 0 2px 10px rgba(0,0,0,0.05); margin-bottom: 2rem; }
.actions-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 1rem; }
.action-btn { display: flex; align-items: center; gap: 0.75rem; padding: 1rem; background: #f8fafc; border: 2px solid #e2e8f0; border-radius: 8px; text-decoration: none; color: #475569; transition: all 0.2s; }
.action-btn:hover { background: #667eea; color: white; border-color: #667eea; }
.recent-jobs { background: white; padding: 2rem; border-radius: 12px; box-shadow: 0 2px 10px rgba(0,0,0,0.05); }
.job-item { padding: 1rem; border-bottom: 1px solid #f1f5f9; display: flex; justify-content: space-between; align-items: center; }
.job-item:last-child { border-bottom: none; }
.job-status { padding: 0.25rem 0.75rem; border-radius: 20px; font-size: 0.8rem; font-weight: 500; }
.status-queued { background: #fef3c7; color: #d97706; }
.status-processing { background: #dbeafe; color: #2563eb; }
.status-completed { background: #d1fae5; color: #065f46; }
.status-failed { background: #fee2e2; color: #dc2626; }
</style>
</head>
<body>
<header class="header">
<div class="header-content">
<div class="logo">VidAI</div>
<div class="user-menu">
<span>Welcome, {{ user.username }}!</span>
<a href="/logout" class="action-btn" style="padding: 0.5rem 1rem; font-size: 0.9rem;">Logout</a>
</div>
</div>
</header>
<div class="container">
<div class="stats-grid">
<div class="stat-card">
<div class="stat-value">{{ tokens }}</div>
<div class="stat-label">Available Tokens</div>
</div>
<div class="stat-card">
<div class="stat-value">{{ completed_jobs }}</div>
<div class="stat-label">Completed Jobs</div>
</div>
<div class="stat-card">
<div class="stat-value">{{ active_jobs }}</div>
<div class="stat-label">Active Jobs</div>
</div>
<div class="stat-card">
<div class="stat-value">{{ total_jobs }}</div>
<div class="stat-label">Total Jobs</div>
</div>
</div>
<div class="quick-actions">
<h3 style="margin-bottom: 1rem; color: #1e293b;">Quick Actions</h3>
<div class="actions-grid">
<a href="/analyze" class="action-btn">
<i class="fas fa-search"></i>
<span>Analyze Media</span>
</a>
<a href="/train" class="action-btn">
<i class="fas fa-graduation-cap"></i>
<span>Train Model</span>
</a>
<a href="/history" class="action-btn">
<i class="fas fa-history"></i>
<span>Job History</span>
</a>
<a href="/settings" class="action-btn">
<i class="fas fa-cog"></i>
<span>Settings</span>
</a>
</div>
</div>
<div class="recent-jobs">
<h3 style="margin-bottom: 1rem; color: #1e293b;">Recent Jobs</h3>
{% for job in queue_items[:5] %}
<div class="job-item">
<div>
<div style="font-weight: 500;">{{ job.request_type.title() }}</div>
<div style="color: #64748b; font-size: 0.9rem;">{{ job.created_at[:19] }}</div>
</div>
<span class="job-status status-{{ job.status }}">{{ job.status.title() }}</span>
</div>
{% endfor %}
{% if queue_items %}
<div style="text-align: center; padding: 1rem; color: #64748b;">{{ queue_items|length }} total jobs</div>
{% else %}
<div style="text-align: center; padding: 1rem; color: #64748b;">No jobs yet</div>
{% endif %}
</div>
</div>
</body>
</html>
\ No newline at end of file
<!DOCTYPE html>
<html>
<head>
<title>Job History - VidAI</title>
<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; background: #f8fafc; }
.header { background: white; padding: 1rem 2rem; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }
.header-content { display: flex; justify-content: space-between; align-items: center; max-width: 1200px; margin: 0 auto; }
.logo { font-size: 1.5rem; font-weight: 700; color: #667eea; }
.nav { display: flex; gap: 2rem; }
.nav a { text-decoration: none; color: #64748b; font-weight: 500; }
.nav a.active { color: #667eea; }
.user-menu { display: flex; align-items: center; gap: 1rem; }
.container { max-width: 1200px; margin: 2rem auto; padding: 0 2rem; }
.history-table { background: white; border-radius: 12px; box-shadow: 0 2px 10px rgba(0,0,0,0.05); overflow: hidden; }
.table-header { padding: 2rem; border-bottom: 1px solid #e5e7eb; }
.table-header h2 { margin: 0; color: #1e293b; }
.job-row { display: grid; grid-template-columns: 1fr 2fr 1fr 100px 100px; gap: 1rem; padding: 1rem 2rem; border-bottom: 1px solid #f1f5f9; align-items: center; }
.job-row:last-child { border-bottom: none; }
.job-type { font-weight: 600; color: #1e293b; }
.job-data { color: #64748b; font-size: 0.9rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.job-time { color: #64748b; font-size: 0.9rem; }
.job-status { padding: 0.25rem 0.75rem; border-radius: 20px; font-size: 0.8rem; font-weight: 500; text-align: center; }
.status-queued { background: #fef3c7; color: #d97706; }
.status-processing { background: #dbeafe; color: #2563eb; }
.status-completed { background: #d1fae5; color: #065f46; }
.status-failed { background: #fee2e2; color: #dc2626; }
.job-tokens { font-weight: 600; color: #667eea; text-align: center; }
.no-jobs { text-align: center; padding: 3rem; color: #6b7280; }
</style>
</head>
<body>
<header class="header">
<div class="header-content">
<div class="logo">VidAI</div>
<nav class="nav">
<a href="/dashboard">Dashboard</a>
<a href="/analyze">Analyze</a>
<a href="/train">Train</a>
<a href="/history" class="active">History</a>
<a href="/settings">Settings</a>
</nav>
<div class="user-menu">
<span>{{ tokens }} tokens</span>
<a href="/logout" style="color: #dc2626;">Logout</a>
</div>
</div>
</header>
<div class="container">
<div class="history-table">
<div class="table-header">
<h2><i class="fas fa-history"></i> Job History</h2>
</div>
{% for job in queue_items %}
<div class="job-row">
<div class="job-type">{{ job.request_type.title() }}</div>
<div class="job-data" title="{{ job.data.get('prompt', job.data.get('description', 'N/A')) }}">
{{ job.data.get('prompt', job.data.get('description', 'N/A'))[:50] }}{% if job.data.get('prompt', job.data.get('description', 'N/A'))|length > 50 %}...{% endif %}
</div>
<div class="job-time">{{ job.created_at[:19] }}</div>
<span class="job-status status-{{ job.status }}">{{ job.status.title() }}</span>
<div class="job-tokens">{{ job.used_tokens or 0 }}</div>
</div>
{% endfor %}
{% if not queue_items %}
<div class="no-jobs">No jobs found. Start by analyzing some media!</div>
{% endif %}
</div>
</div>
</body>
</html>
\ No newline at end of file
...@@ -3,8 +3,9 @@ ...@@ -3,8 +3,9 @@
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Video AI Analysis - Professional Content Creation Tools</title> <title>VidAI - The adult content friendly video automation system</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."> <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 rel="icon" type="image/jpg" href="/image.jpg">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet"> <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"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
<style> <style>
...@@ -398,8 +399,8 @@ ...@@ -398,8 +399,8 @@
<body> <body>
<header> <header>
<div class="container"> <div class="container">
<div class="logo">Video AI Pro</div> <div class="logo">VidAI</div>
<div class="tagline">Professional AI-Powered Video Analysis for Content Creators</div> <div class="tagline">The adult content friendly video automation system</div>
</div> </div>
</header> </header>
...@@ -494,8 +495,8 @@ ...@@ -494,8 +495,8 @@
<div class="container"> <div class="container">
<div class="footer-content"> <div class="footer-content">
<div class="footer-section"> <div class="footer-section">
<h3>Video AI Pro</h3> <h3>VidAI</h3>
<p>Professional AI-powered video analysis tools for content creators worldwide. Built with privacy, security, and performance in mind.</p> <p>The adult content friendly video automation system. Professional AI-powered video analysis tools for content creators worldwide. Built with privacy, security, and performance in mind.</p>
</div> </div>
<div class="footer-section"> <div class="footer-section">
...@@ -523,7 +524,7 @@ ...@@ -523,7 +524,7 @@
</div> </div>
<div class="copyright"> <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>&copy; 2024 VidAI. 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://git.nexlab.net/SexHackMe/vidai" style="color: #667eea;">View Source</a></p> <p>Licensed under <a href="/license" style="color: #667eea;">GPLv3</a> | <a href="https://git.nexlab.net/SexHackMe/vidai" style="color: #667eea;">View Source</a></p>
</div> </div>
</div> </div>
......
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<title>Login - Video AI Pro</title> <title>Login - VidAI</title>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet"> <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
<style> <style>
* { margin: 0; padding: 0; box-sizing: border-box; } * { margin: 0; padding: 0; box-sizing: border-box; }
...@@ -23,7 +23,7 @@ ...@@ -23,7 +23,7 @@
</head> </head>
<body> <body>
<div class="login-container"> <div class="login-container">
<div class="logo">Video AI Pro</div> <div class="logo">VidAI</div>
<form method="post"> <form method="post">
{% with messages = get_flashed_messages(with_categories=true) %} {% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %} {% if messages %}
......
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<title>Register - Video AI Pro</title> <title>Register - VidAI</title>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet"> <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
<style> <style>
* { margin: 0; padding: 0; box-sizing: border-box; } * { margin: 0; padding: 0; box-sizing: border-box; }
...@@ -23,7 +23,7 @@ ...@@ -23,7 +23,7 @@
</head> </head>
<body> <body>
<div class="register-container"> <div class="register-container">
<div class="logo">Video AI Pro</div> <div class="logo">VidAI</div>
<form method="post"> <form method="post">
{% with messages = get_flashed_messages(with_categories=true) %} {% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %} {% if messages %}
......
<!DOCTYPE html>
<html>
<head>
<title>Settings - VidAI</title>
<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; background: #f8fafc; }
.header { background: white; padding: 1rem 2rem; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }
.header-content { display: flex; justify-content: space-between; align-items: center; max-width: 1200px; margin: 0 auto; }
.logo { font-size: 1.5rem; font-weight: 700; color: #667eea; }
.nav { display: flex; gap: 2rem; }
.nav a { text-decoration: none; color: #64748b; font-weight: 500; }
.nav a.active { color: #667eea; }
.user-menu { display: flex; align-items: center; gap: 1rem; }
.container { max-width: 800px; margin: 2rem auto; padding: 0 2rem; }
.settings-card { background: white; padding: 2rem; border-radius: 12px; box-shadow: 0 2px 10px rgba(0,0,0,0.05); margin-bottom: 2rem; }
.card-header { margin-bottom: 1.5rem; }
.card-header h3 { margin: 0; color: #1e293b; }
.form-group { margin-bottom: 1.5rem; }
.form-group label { display: block; margin-bottom: 0.5rem; color: #374151; font-weight: 500; }
.form-group input, .form-group select { width: 100%; padding: 0.75rem; border: 2px solid #e5e7eb; border-radius: 8px; font-size: 1rem; }
.form-group input:focus, .form-group select:focus { outline: none; border-color: #667eea; }
.btn { padding: 0.75rem 2rem; background: #667eea; color: white; border: none; border-radius: 8px; font-size: 1rem; font-weight: 600; cursor: pointer; }
.btn:hover { background: #5a67d8; }
.alert { padding: 0.75rem; border-radius: 8px; margin-bottom: 1rem; }
.alert-error { background: #fee2e2; color: #dc2626; border: 1px solid #fecaca; }
.alert-success { background: #d1fae5; color: #065f46; border: 1px solid #a7f3d0; }
.user-info { background: #f0f9ff; padding: 1.5rem; border-radius: 8px; border-left: 4px solid #667eea; margin-bottom: 2rem; }
</style>
</head>
<body>
<header class="header">
<div class="header-content">
<div class="logo">VidAI</div>
<nav class="nav">
<a href="/dashboard">Dashboard</a>
<a href="/analyze">Analyze</a>
<a href="/train">Train</a>
<a href="/history">History</a>
<a href="/settings" class="active">Settings</a>
</nav>
<div class="user-menu">
<span>{{ tokens }} tokens</span>
<a href="/logout" style="color: #dc2626;">Logout</a>
</div>
</div>
</header>
<div class="container">
<div class="user-info">
<h3><i class="fas fa-user"></i> Account Information</h3>
<p><strong>Username:</strong> {{ user.username }}</p>
<p><strong>Email:</strong> {{ user.email or "Not provided" }}</p>
<p><strong>Role:</strong> {{ user.role.title() }}</p>
<p><strong>Member since:</strong> {{ user.created_at[:10] }}</p>
</div>
<div class="settings-card">
<div class="card-header">
<h3><i class="fas fa-cog"></i> Application Settings</h3>
</div>
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
{% for category, message in messages %}
<div class="alert alert-{{ 'error' if category == 'error' else 'success' }}">{{ message }}</div>
{% endfor %}
{% endif %}
{% endwith %}
<form method="post" action="/update_settings">
<div class="form-group">
<label for="default_model">Default AI Model</label>
<input type="text" id="default_model" name="default_model" value="Qwen/Qwen2.5-VL-7B-Instruct" placeholder="Model path or HuggingFace ID">
</div>
<div class="form-group">
<label for="theme">Theme Preference</label>
<select id="theme" name="theme">
<option value="light" selected>Light</option>
<option value="dark">Dark</option>
</select>
</div>
<button type="submit" class="btn"><i class="fas fa-save"></i> Save Settings</button>
</form>
</div>
<div class="settings-card">
<div class="card-header">
<h3><i class="fas fa-key"></i> API Access</h3>
</div>
<p>Generate API tokens to access VidAI programmatically.</p>
<a href="/generate_token" class="btn" style="background: #10b981;"><i class="fas fa-plus"></i> Generate New Token</a>
</div>
</div>
</body>
</html>
\ No newline at end of file
<!DOCTYPE html>
<html>
<head>
<title>Train Model - VidAI</title>
<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; background: #f8fafc; }
.header { background: white; padding: 1rem 2rem; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }
.header-content { display: flex; justify-content: space-between; align-items: center; max-width: 1200px; margin: 0 auto; }
.logo { font-size: 1.5rem; font-weight: 700; color: #667eea; }
.nav { display: flex; gap: 2rem; }
.nav a { text-decoration: none; color: #64748b; font-weight: 500; }
.nav a.active { color: #667eea; }
.user-menu { display: flex; align-items: center; gap: 1rem; }
.container { max-width: 800px; margin: 2rem auto; padding: 0 2rem; }
.training-form { background: white; padding: 2rem; border-radius: 12px; box-shadow: 0 2px 10px rgba(0,0,0,0.05); }
.form-group { margin-bottom: 1.5rem; }
.form-group label { display: block; margin-bottom: 0.5rem; color: #374151; font-weight: 500; }
.form-group input, .form-group textarea { width: 100%; padding: 0.75rem; border: 2px solid #e5e7eb; border-radius: 8px; font-size: 1rem; }
.form-group input:focus, .form-group textarea:focus { outline: none; border-color: #667eea; }
.form-group textarea { resize: vertical; min-height: 100px; }
.btn { padding: 0.75rem 2rem; background: #667eea; color: white; border: none; border-radius: 8px; font-size: 1rem; font-weight: 600; cursor: pointer; }
.btn:hover { background: #5a67d8; }
.result { background: white; padding: 2rem; border-radius: 12px; box-shadow: 0 2px 10px rgba(0,0,0,0.05); margin-top: 2rem; }
.alert { padding: 0.75rem; border-radius: 8px; margin-bottom: 1rem; }
.alert-error { background: #fee2e2; color: #dc2626; border: 1px solid #fecaca; }
.alert-success { background: #d1fae5; color: #065f46; border: 1px solid #a7f3d0; }
.tokens { background: #f0f9ff; padding: 1rem; border-radius: 8px; margin-bottom: 1rem; border-left: 4px solid #667eea; }
.warning { background: #fef3c7; padding: 1rem; border-radius: 8px; margin-bottom: 1rem; border-left: 4px solid #d97706; }
</style>
</head>
<body>
<header class="header">
<div class="header-content">
<div class="logo">VidAI</div>
<nav class="nav">
<a href="/dashboard">Dashboard</a>
<a href="/analyze">Analyze</a>
<a href="/train" class="active">Train</a>
<a href="/history">History</a>
<a href="/settings">Settings</a>
</nav>
<div class="user-menu">
<span>{{ tokens }} tokens</span>
<a href="/logout" style="color: #dc2626;">Logout</a>
</div>
</div>
</header>
<div class="container">
<div class="tokens">
<strong>Available Tokens:</strong> {{ tokens }} (Training costs ~100 tokens)
</div>
<div class="warning">
<strong><i class="fas fa-exclamation-triangle"></i> Training Notice:</strong> Model training requires significant computational resources and may take several hours to complete. Ensure you have sufficient tokens and system resources.
</div>
<div class="training-form">
<h2 style="margin-bottom: 1.5rem; color: #1e293b;"><i class="fas fa-graduation-cap"></i> Train Custom Model</h2>
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
{% for category, message in messages %}
<div class="alert alert-{{ 'error' if category == 'error' else 'success' }}">{{ message }}</div>
{% endfor %}
{% endif %}
{% endwith %}
<form method="post" enctype="multipart/form-data">
<div class="form-group">
<label for="output_model">Output Model Name</label>
<input type="text" id="output_model" name="output_model" value="MyCustomModel" placeholder="Name for your trained model">
<small style="color: #6b7280;">Choose a unique name for your custom model</small>
</div>
<div class="form-group">
<label for="data">Upload Training Data</label>
<input type="file" id="data" name="data" accept=".zip,image/*,video/*">
<small style="color: #6b7280;">Upload a ZIP file containing training images/videos, or individual media files</small>
</div>
<div class="form-group">
<label for="train_dir">Or Training Directory Path</label>
<input type="text" id="train_dir" name="train_dir" placeholder="/path/to/training/data">
<small style="color: #6b7280;">Path to local directory containing training data</small>
</div>
<div class="form-group">
<label for="description">Training Description</label>
<textarea id="description" name="description" placeholder="Describe what you want the model to learn...">Train a custom model to recognize specific objects, scenes, or patterns in images and videos.</textarea>
<small style="color: #6b7280;">Provide context about what the model should learn from the training data</small>
</div>
<button type="submit" class="btn"><i class="fas fa-rocket"></i> Start Training</button>
</form>
</div>
{% if message %}
<div class="result">
<h3 style="margin-bottom: 1rem; color: #1e293b;"><i class="fas fa-check-circle"></i> Training Status</h3>
<div style="background: #f8fafc; padding: 1rem; border-radius: 8px; border-left: 4px solid #10b981;">
<p style="margin: 0;">{{ message }}</p>
</div>
</div>
{% endif %}
</div>
</body>
</html>
\ No newline at end of file
...@@ -29,7 +29,7 @@ from .config import get_all_settings, get_allow_registration ...@@ -29,7 +29,7 @@ from .config import get_all_settings, get_allow_registration
from .auth import login_user, logout_user, get_current_user, register_user, confirm_email, require_auth, require_admin from .auth import login_user, logout_user, get_current_user, register_user, confirm_email, require_auth, require_admin
from .database import get_user_tokens, update_user_tokens, get_user_queue_items, get_default_user_tokens from .database import get_user_tokens, update_user_tokens, get_user_queue_items, get_default_user_tokens
app = Flask(__name__, template_folder='../templates') app = Flask(__name__, template_folder=os.path.join(os.path.dirname(__file__), '..', 'templates'))
app.secret_key = os.environ.get('FLASK_SECRET_KEY', 'dev-secret-key-change-in-production') app.secret_key = os.environ.get('FLASK_SECRET_KEY', 'dev-secret-key-change-in-production')
os.makedirs('static', exist_ok=True) os.makedirs('static', exist_ok=True)
...@@ -388,5 +388,9 @@ def stats(): ...@@ -388,5 +388,9 @@ def stats():
def serve_static(filename): def serve_static(filename):
return send_from_directory('static', filename) return send_from_directory('static', filename)
@app.route('/image.jpg')
def serve_logo():
return send_from_directory('.', 'image.jpg')
if __name__ == "__main__": if __name__ == "__main__":
app.run(host='0.0.0.0', debug=True) app.run(host='0.0.0.0', debug=True)
\ No newline at end of file
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