You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
RedditVideoMakerBot/GUI/settings.html

391 lines
18 KiB

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Settings - Reddit Video Maker Bot</title>
<link rel="stylesheet" href="/static/css/app.css">
</head>
<body>
<nav class="sidebar">
<div class="logo">
<h2>Reddit Video Bot</h2>
</div>
<ul class="nav-links">
<li><a href="/">Dashboard</a></li>
<li><a href="/settings" class="active">Settings</a></li>
<li><a href="/backgrounds">Backgrounds</a></li>
<li><a href="/videos">Videos</a></li>
<li><a href="/progress">Progress</a></li>
</ul>
</nav>
<main class="content">
<header>
<h1>Settings</h1>
<button id="btn-save" class="btn btn-primary">Save All Settings</button>
</header>
<form id="settings-form">
<!-- Qwen TTS Settings -->
<div class="card">
<h3>Qwen TTS Settings</h3>
<p class="card-description">Configure your Qwen TTS server connection for text-to-speech generation.</p>
<div class="form-group">
<label for="qwen_api_url">API URL</label>
<input type="url" id="qwen_api_url" name="qwen_api_url" placeholder="http://localhost:8080">
<small>The base URL of your Qwen TTS server</small>
</div>
<div class="form-row">
<div class="form-group">
<label for="qwen_email">Email</label>
<input type="email" id="qwen_email" name="qwen_email" placeholder="your@email.com">
</div>
<div class="form-group">
<label for="qwen_password">Password</label>
<input type="password" id="qwen_password" name="qwen_password" placeholder="Enter password">
</div>
</div>
<div class="form-row">
<div class="form-group">
<label for="qwen_speaker">Speaker Voice</label>
<select id="qwen_speaker" name="qwen_speaker">
<option value="Chelsie">Chelsie</option>
<option value="Ethan">Ethan</option>
<option value="Vivian" selected>Vivian</option>
<option value="Asher">Asher</option>
<option value="Aria">Aria</option>
<option value="Oliver">Oliver</option>
<option value="Emma">Emma</option>
<option value="Noah">Noah</option>
<option value="Sophia">Sophia</option>
</select>
</div>
<div class="form-group">
<label for="qwen_language">Language</label>
<select id="qwen_language" name="qwen_language">
<option value="English" selected>English</option>
<option value="Chinese">Chinese</option>
<option value="Spanish">Spanish</option>
<option value="French">French</option>
<option value="German">German</option>
<option value="Japanese">Japanese</option>
<option value="Korean">Korean</option>
</select>
</div>
</div>
<div class="form-group">
<label for="qwen_instruct">Voice Style Instructions</label>
<input type="text" id="qwen_instruct" name="qwen_instruct" placeholder="Warm, friendly, conversational.">
<small>Describe the speaking style you want</small>
</div>
<button type="button" id="btn-test-tts" class="btn btn-secondary">Test TTS Connection</button>
<span id="tts-status" class="status-text"></span>
</div>
<!-- Reddit Settings -->
<div class="card">
<h3>Reddit Settings</h3>
<p class="card-description">Configure which subreddits to scrape for content. No API keys required!</p>
<div class="form-group">
<label for="subreddit">Subreddit</label>
<div class="input-with-prefix">
<span class="prefix">r/</span>
<input type="text" id="subreddit" name="subreddit" placeholder="AskReddit">
</div>
<small>Enter subreddit name without r/ prefix. Use + for multiple (e.g., AskReddit+nosleep)</small>
</div>
<div class="form-row">
<div class="form-group">
<label for="min_comments">Minimum Comments</label>
<input type="number" id="min_comments" name="min_comments" value="20" min="1">
</div>
<div class="form-group">
<label for="max_comment_length">Max Comment Length</label>
<input type="number" id="max_comment_length" name="max_comment_length" value="500" min="50">
</div>
</div>
<div class="form-group">
<label for="post_id">Specific Post ID (Optional)</label>
<input type="text" id="post_id" name="post_id" placeholder="Leave empty for random">
<small>Enter a specific Reddit post ID, or leave empty to fetch random posts</small>
</div>
<div class="form-group checkbox-group">
<label>
<input type="checkbox" id="random" name="random" checked>
Pick random posts from subreddit
</label>
</div>
<div class="form-group checkbox-group">
<label>
<input type="checkbox" id="allow_nsfw" name="allow_nsfw">
Allow NSFW content
</label>
</div>
</div>
<!-- Video Settings -->
<div class="card">
<h3>Video Settings</h3>
<p class="card-description">Configure video output and visual settings.</p>
<div class="form-row">
<div class="form-group">
<label for="resolution_w">Width</label>
<input type="number" id="resolution_w" name="resolution_w" value="1080">
</div>
<div class="form-group">
<label for="resolution_h">Height</label>
<input type="number" id="resolution_h" name="resolution_h" value="1920">
</div>
</div>
<div class="form-row">
<div class="form-group">
<label for="theme">Screenshot Theme</label>
<select id="theme" name="theme">
<option value="dark" selected>Dark</option>
<option value="light">Light</option>
<option value="transparent">Transparent</option>
</select>
</div>
<div class="form-group">
<label for="opacity">Opacity</label>
<input type="number" id="opacity" name="opacity" value="0.9" min="0" max="1" step="0.1">
</div>
</div>
<div class="form-group">
<label for="times_to_run">Videos per Run</label>
<input type="number" id="times_to_run" name="times_to_run" value="1" min="1" max="10">
<small>How many videos to generate when you click "Generate"</small>
</div>
<div class="form-group checkbox-group">
<label>
<input type="checkbox" id="storymode" name="storymode">
Story Mode (for narrative subreddits like r/nosleep)
</label>
</div>
</div>
<!-- Background Settings -->
<div class="card">
<h3>Background Settings</h3>
<p class="card-description">Select background video and audio. Upload custom backgrounds in the Backgrounds page.</p>
<div class="form-row">
<div class="form-group">
<label for="background_video">Background Video</label>
<select id="background_video" name="background_video">
<option value="minecraft">Minecraft Parkour</option>
<option value="gta">GTA V</option>
<option value="rocket-league">Rocket League</option>
<option value="subway-surfers">Subway Surfers</option>
</select>
</div>
<div class="form-group">
<label for="background_audio">Background Audio</label>
<select id="background_audio" name="background_audio">
<option value="lofi">Lo-Fi Beats</option>
<option value="lofi-2">Lo-Fi Beats 2</option>
<option value="chill-summer">Chill Summer</option>
<option value="none">No Background Audio</option>
</select>
</div>
</div>
<div class="form-group">
<label for="background_audio_volume">Background Audio Volume</label>
<input type="range" id="background_audio_volume" name="background_audio_volume" min="0" max="1" step="0.05" value="0.15">
<span id="volume-display">15%</span>
</div>
</div>
</form>
</main>
<script src="/static/js/app.js"></script>
<script>
document.addEventListener('DOMContentLoaded', () => {
loadSettings();
loadCustomBackgrounds();
document.getElementById('btn-save').addEventListener('click', saveSettings);
document.getElementById('btn-test-tts').addEventListener('click', testTTSConnection);
// Volume slider display
const volumeSlider = document.getElementById('background_audio_volume');
const volumeDisplay = document.getElementById('volume-display');
volumeSlider.addEventListener('input', () => {
volumeDisplay.textContent = Math.round(volumeSlider.value * 100) + '%';
});
});
function loadSettings() {
fetch('/api/config')
.then(r => r.json())
.then(config => {
if (!config || !config.settings) return;
// TTS settings
const tts = config.settings.tts || {};
setFieldValue('qwen_api_url', tts.qwen_api_url);
setFieldValue('qwen_email', tts.qwen_email);
setFieldValue('qwen_speaker', tts.qwen_speaker);
setFieldValue('qwen_language', tts.qwen_language);
setFieldValue('qwen_instruct', tts.qwen_instruct);
// Reddit settings
const thread = config.reddit?.thread || {};
setFieldValue('subreddit', thread.subreddit);
setFieldValue('min_comments', thread.min_comments);
setFieldValue('max_comment_length', thread.max_comment_length);
setFieldValue('post_id', thread.post_id);
setCheckbox('random', thread.random);
setCheckbox('allow_nsfw', config.settings.allow_nsfw);
// Video settings
setFieldValue('resolution_w', config.settings.resolution_w);
setFieldValue('resolution_h', config.settings.resolution_h);
setFieldValue('theme', config.settings.theme);
setFieldValue('opacity', config.settings.opacity);
setFieldValue('times_to_run', config.settings.times_to_run);
setCheckbox('storymode', config.settings.storymode);
// Background settings
const bg = config.settings.background || {};
setFieldValue('background_video', bg.background_video);
setFieldValue('background_audio', bg.background_audio);
setFieldValue('background_audio_volume', bg.background_audio_volume);
// Update volume display
const vol = bg.background_audio_volume || 0.15;
document.getElementById('volume-display').textContent = Math.round(vol * 100) + '%';
})
.catch(err => console.error('Error loading settings:', err));
}
function loadCustomBackgrounds() {
fetch('/api/backgrounds/video')
.then(r => r.json())
.then(data => {
const videoSelect = document.getElementById('background_video');
// Add custom videos
if (data.videos && data.videos.length > 0) {
const customGroup = document.createElement('optgroup');
customGroup.label = 'Custom Videos';
data.videos.forEach(v => {
const opt = document.createElement('option');
opt.value = 'custom:' + v.filename;
opt.textContent = v.name;
customGroup.appendChild(opt);
});
videoSelect.appendChild(customGroup);
}
})
.catch(err => console.error('Error loading backgrounds:', err));
}
function setFieldValue(id, value) {
const field = document.getElementById(id);
if (field && value !== undefined && value !== null) {
field.value = value;
}
}
function setCheckbox(id, value) {
const field = document.getElementById(id);
if (field) {
field.checked = !!value;
}
}
function saveSettings() {
const form = document.getElementById('settings-form');
const config = {
subreddit: form.subreddit.value,
post_id: form.post_id.value,
min_comments: parseInt(form.min_comments.value),
max_comment_length: parseInt(form.max_comment_length.value),
random: form.random.checked,
allow_nsfw: form.allow_nsfw.checked,
theme: form.theme.value,
opacity: parseFloat(form.opacity.value),
times_to_run: parseInt(form.times_to_run.value),
storymode: form.storymode.checked,
resolution_w: parseInt(form.resolution_w.value),
resolution_h: parseInt(form.resolution_h.value),
voice_choice: 'qwentts',
qwen_api_url: form.qwen_api_url.value,
qwen_email: form.qwen_email.value,
qwen_password: form.qwen_password.value,
qwen_speaker: form.qwen_speaker.value,
qwen_language: form.qwen_language.value,
qwen_instruct: form.qwen_instruct.value,
background_video: form.background_video.value,
background_audio: form.background_audio.value,
background_audio_volume: parseFloat(form.background_audio_volume.value)
};
fetch('/api/config', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(config)
})
.then(r => r.json())
.then(data => {
if (data.success) {
showNotification('Settings saved successfully!', 'success');
} else {
showNotification('Failed to save: ' + data.message, 'error');
}
})
.catch(err => showNotification('Error saving settings', 'error'));
}
function testTTSConnection() {
const statusEl = document.getElementById('tts-status');
statusEl.textContent = 'Testing...';
statusEl.className = 'status-text';
const config = {
qwen_api_url: document.getElementById('qwen_api_url').value,
qwen_email: document.getElementById('qwen_email').value,
qwen_password: document.getElementById('qwen_password').value
};
fetch('/api/tts/test', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(config)
})
.then(r => r.json())
.then(data => {
if (data.success) {
statusEl.textContent = 'Connection successful!';
statusEl.className = 'status-text success';
} else {
statusEl.textContent = 'Failed: ' + data.message;
statusEl.className = 'status-text error';
}
})
.catch(err => {
statusEl.textContent = 'Connection error';
statusEl.className = 'status-text error';
});
}
</script>
</body>
</html>