Teletube / templates /dashboard.html
NitinBot002's picture
Rename templates/auth.html to templates/dashboard.html
bc131a3 verified
{% extends "base.html" %}
{% block title %}Dashboard - Telegram to YouTube Uploader{% endblock %}
{% block content %}
<h2>Dashboard</h2>
<!-- Configuration Section -->
<div class="card mb-4">
<div class="card-header">
<h5>Configuration</h5>
</div>
<div class="card-body">
<form id="configForm">
<div class="row">
<div class="col-md-6 mb-3">
<label for="telegram_channel_username" class="form-label">Telegram Channel Username *</label>
<input type="text" class="form-control" id="telegram_channel_username" name="telegram_channel_username" value="{{ config.TELEGRAM_CHANNEL_USERNAME or '' }}" placeholder="@your_channel">
<div class="form-text">The username of the Telegram channel to monitor (e.g., @mychannel).</div>
</div>
<div class="col-md-6 mb-3">
<label for="youtube_title_prefix" class="form-label">YouTube Title Prefix</label>
<input type="text" class="form-control" id="youtube_title_prefix" name="youtube_title_prefix" value="{{ config.YOUTUBE_TITLE_PREFIX or '[TG2YT] ' }}">
<div class="form-text">Text to prepend to the Telegram video caption for the YouTube title.</div>
</div>
</div>
<div class="mb-3">
<label for="youtube_description_template" class="form-label">YouTube Description Template</label>
<textarea class="form-control" id="youtube_description_template" name="youtube_description_template" rows="2">{{ config.YOUTUBE_DESCRIPTION_TEMPLATE or 'Video automatically uploaded from Telegram.' }}</textarea>
<div class="form-text">Base description for uploaded videos. Original Telegram post link and caption will be appended.</div>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label for="youtube_tags" class="form-label">YouTube Tags</label>
<input type="text" class="form-control" id="youtube_tags" name="youtube_tags" value="{{ config.YOUTUBE_TAGS or 'telegram,upload,automation' }}">
<div class="form-text">Comma-separated list of tags for YouTube videos.</div>
</div>
<div class="col-md-3 mb-3">
<label for="youtube_category_id" class="form-label">YouTube Category ID</label>
<input type="text" class="form-control" id="youtube_category_id" name="youtube_category_id" value="{{ config.YOUTUBE_CATEGORY_ID or '22' }}">
<div class="form-text">Numeric ID for the YouTube video category (22 = People & Blogs).</div>
</div>
<div class="col-md-3 mb-3">
<label for="youtube_privacy_status" class="form-label">YouTube Privacy Status</label>
<select class="form-select" id="youtube_privacy_status" name="youtube_privacy_status">
<option value="private" {% if (config.YOUTUBE_PRIVACY_STATUS or 'private') == 'private' %}selected{% endif %}>Private</option>
<option value="public" {% if (config.YOUTUBE_PRIVACY_STATUS or 'private') == 'public' %}selected{% endif %}>Public</option>
<option value="unlisted" {% if (config.YOUTUBE_PRIVACY_STATUS or 'private') == 'unlisted' %}selected{% endif %}>Unlisted</option>
</select>
<div class="form-text">Initial privacy setting for uploaded videos.</div>
</div>
</div>
<button type="submit" class="btn btn-primary" id="saveConfigBtn">Save Configuration</button>
<div id="configSaveStatus" class="mt-2"></div>
</form>
</div>
</div>
<!-- Existing Status and Control Sections -->
<div class="row">
<div class="col-md-6">
<div class="card status-card">
<div class="card-header">
<h5>Authentication Status</h5>
</div>
<div class="card-body">
<p><strong>YouTube:</strong>
{% if status.youtube_authenticated %}
<span class="badge bg-success">Authenticated</span>
<a href="{{ url_for('youtube_auth') }}" class="btn btn-sm btn-outline-primary">Re-authenticate</a>
{% else %}
<span class="badge bg-warning text-dark">Not Authenticated</span>
<a href="{{ url_for('youtube_auth') }}" class="btn btn-sm btn-primary">Authenticate</a>
{% endif %}
</p>
<p><strong>Telegram:</strong>
{% if status.telegram_authenticated %}
<span class="badge bg-success">Connected</span>
<a href="{{ url_for('telegram_auth') }}" class="btn btn-sm btn-outline-primary">Re-authenticate</a>
{% else %}
<span class="badge bg-warning text-dark">Not Connected</span>
<a href="{{ url_for('telegram_auth') }}" class="btn btn-sm btn-primary">Connect</a>
{% endif %}
</p>
</div>
</div>
<div class="card status-card">
<div class="card-header">
<h5>Processing Control</h5>
</div>
<div class="card-body">
<form id="startForm">
<div class="mb-3">
<label for="limit" class="form-label">Batch Size (Videos per batch)</label>
<input type="number" class="form-control" id="limit" name="limit" value="5" min="1" max="50">
</div>
<button type="submit" class="btn btn-success" id="startBtn" {% if status.is_running %}disabled{% endif %}>Start Processing</button>
<button type="button" class="btn btn-danger" id="stopBtn" {% if not status.is_running %}disabled{% endif %}>Stop Processing</button>
</form>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card status-card">
<div class="card-header">
<h5>Current Status</h5>
</div>
<div class="card-body">
<p><strong>Running:</strong>
{% if status.is_running %}
<span class="badge bg-success">Yes</span>
{% else %}
<span class="badge bg-secondary">No</span>
{% endif %}
</p>
<p><strong>Current Batch:</strong> {{ status.current_batch }}</p>
<p><strong>Processed in Session:</strong> {{ status.processed_count }}</p>
<p><strong>Failed in Session:</strong> {{ status.failed_count }}</p>
<p><strong>Total Processed (Ever):</strong> {{ status.total_processed }}</p>
<div id="confirmationArea">
<!-- Confirmation prompt will be inserted here by JavaScript -->
</div>
</div>
</div>
</div>
</div>
<div class="row mt-4">
<div class="col-12">
<div class="card">
<div class="card-header d-flex justify-content-between align-items-center">
<h5>Processing Logs</h5>
<button class="btn btn-sm btn-outline-secondary" id="clearLogsBtn">Clear Logs</button>
</div>
<div class="card-body">
<div class="log-container" id="logContainer">
<!-- Logs will be populated here by JavaScript -->
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block scripts %}
<script>
// --- Configuration Saving Logic ---
document.getElementById('configForm').addEventListener('submit', function(e) {
e.preventDefault();
const saveBtn = document.getElementById('saveConfigBtn');
const statusDiv = document.getElementById('configSaveStatus');
const originalBtnText = saveBtn.textContent;
saveBtn.disabled = true;
saveBtn.textContent = 'Saving...';
statusDiv.innerHTML = '<div class="spinner-border spinner-border-sm" role="status"><span class="visually-hidden">Saving...</span></div>';
const formData = new FormData(this);
fetch('/save_config', {
method: 'POST',
body: formData
})
.then(response => {
if (!response.ok) {
return response.json().then(err => { throw new Error(err.message || 'Network response was not ok'); });
}
return response.json();
})
.then(data => {
if (data.success) {
statusDiv.innerHTML = '<div class="alert alert-success alert-dismissible fade show" role="alert">Configuration saved successfully!<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button></div>';
// Optionally, re-fetch status to update any dependent UI elements if needed
// updateStatus(); // If you add config status checks to the main status endpoint
} else {
statusDiv.innerHTML = `<div class="alert alert-danger alert-dismissible fade show" role="alert">Error saving configuration: ${data.message}<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button></div>`;
}
})
.catch(error => {
console.error('Error:', error);
statusDiv.innerHTML = `<div class="alert alert-danger alert-dismissible fade show" role="alert">Error saving configuration: ${error.message}<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button></div>`;
})
.finally(() => {
saveBtn.disabled = false;
saveBtn.textContent = originalBtnText;
// Auto-hide success message after a few seconds
const successAlert = statusDiv.querySelector('.alert-success');
if (successAlert) {
setTimeout(() => {
if (successAlert.parentNode === statusDiv) { // Check if still present
successAlert.remove();
}
}, 3000);
}
});
});
// --- Existing Status and Control Logic ---
let isWaitingForConfirmation = false;
function updateStatus() {
fetch('/status')
.then(response => response.json())
.then(data => {
// Update status indicators
document.getElementById('startBtn').disabled = data.is_running;
document.getElementById('stopBtn').disabled = !data.is_running;
// Update log container
const logContainer = document.getElementById('logContainer');
logContainer.innerHTML = '';
data.logs.forEach(log => {
const logEntry = document.createElement('div');
logEntry.textContent = `[${log.timestamp}] ${log.level}: ${log.message}`;
logEntry.classList.add('mb-1');
if (log.level === 'ERROR') logEntry.classList.add('text-danger');
else if (log.level === 'WARNING') logEntry.classList.add('text-warning');
logContainer.appendChild(logEntry);
});
logContainer.scrollTop = logContainer.scrollHeight; // Auto-scroll to bottom
// Handle confirmation prompt
const confirmationArea = document.getElementById('confirmationArea');
if (data.waiting_for_confirmation && !isWaitingForConfirmation) {
isWaitingForConfirmation = true;
confirmationArea.innerHTML = `
<div class="alert alert-info confirmation-prompt" role="alert">
<strong>Confirmation Needed:</strong> ${data.confirmation_message}
<div class="mt-2">
<button class="btn btn-sm btn-success confirm-btn" data-action="continue">Continue</button>
<button class="btn btn-sm btn-secondary confirm-btn" data-action="stop">Stop</button>
</div>
</div>
`;
} else if (!data.waiting_for_confirmation && isWaitingForConfirmation) {
isWaitingForConfirmation = false;
confirmationArea.innerHTML = ''; // Clear prompt
}
})
.catch(error => console.error('Error fetching status:', error));
}
// Initial update
updateStatus();
// Update status every 2 seconds
const statusInterval = setInterval(updateStatus, 2000);
// Event Listeners for Processing Control
document.getElementById('startForm').addEventListener('submit', function(e) {
e.preventDefault();
const formData = new FormData(this);
fetch('/start_processing', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (data.success) {
// updateStatus(); // Will be updated by interval
// Provide immediate feedback
const startBtn = document.getElementById('startBtn');
startBtn.disabled = true;
} else {
alert(data.message);
}
})
.catch(error => console.error('Error starting processing:', error));
});
document.getElementById('stopBtn').addEventListener('click', function() {
fetch('/stop_processing', {
method: 'POST'
})
.then(response => response.json())
.then(data => {
if (data.success) {
// updateStatus(); // Will be updated by interval
// Provide immediate feedback
const stopBtn = document.getElementById('stopBtn');
stopBtn.disabled = true;
} else {
alert(data.message);
}
})
.catch(error => console.error('Error stopping processing:', error));
});
document.getElementById('clearLogsBtn').addEventListener('click', function() {
fetch('/clear_logs', {
method: 'POST'
})
.then(response => response.json())
.then(data => {
if (data.success) {
const logContainer = document.getElementById('logContainer');
logContainer.innerHTML = '';
}
})
.catch(error => console.error('Error clearing logs:', error));
});
// Confirmation buttons (using event delegation)
document.addEventListener('click', function(e) {
if (e.target.classList.contains('confirm-btn')) {
const action = e.target.getAttribute('data-action');
fetch('/batch_confirmation', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: `action=${action}`
})
.then(response => response.json())
.then(data => {
if (data.success) {
// updateStatus(); // Will be updated by interval
// Optionally, clear the prompt immediately
document.getElementById('confirmationArea').innerHTML = '';
isWaitingForConfirmation = false;
} else {
alert(data.message);
}
})
.catch(error => console.error('Error sending confirmation:', error));
}
});
// Clear interval on page unload (optional, good practice)
window.addEventListener('beforeunload', () => {
clearInterval(statusInterval);
});
</script>
{% endblock %}