Updates
This commit is contained in:
parent
e86ab53de5
commit
095bf52a2f
29 changed files with 2494 additions and 758 deletions
|
@ -8,33 +8,70 @@
|
|||
{% block extra_head %}{% endblock %}
|
||||
</head>
|
||||
<body>
|
||||
<div class="layout-container">
|
||||
<!-- Header with app name and search -->
|
||||
<header class="main-header">
|
||||
<div class="logo">
|
||||
<div class="app-container">
|
||||
<!-- Sidebar -->
|
||||
<nav class="sidebar">
|
||||
<div class="sidebar-header">
|
||||
<h1>Podcastrr</h1>
|
||||
</div>
|
||||
<div class="search-container">
|
||||
<form action="{{ url_for('podcasts.search') }}" method="post" class="header-search-form">
|
||||
<input type="text" name="query" placeholder="Search podcasts...">
|
||||
<button type="submit" class="btn btn-search">Search</button>
|
||||
</form>
|
||||
<div class="sidebar-nav">
|
||||
<ul>
|
||||
<li>
|
||||
<a href="{{ url_for('main.index') }}"
|
||||
class="{% if request.endpoint == 'main.index' %}active{% endif %}">
|
||||
Home
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{{ url_for('podcasts.index') }}"
|
||||
class="{% if request.endpoint in ['podcasts.index', 'podcasts.view'] %}active{% endif %}">
|
||||
Podcasts
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{{ url_for('podcasts.search') }}"
|
||||
class="{% if request.endpoint == 'podcasts.search' %}active{% endif %}">
|
||||
Add New
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{{ url_for('main.dashboard') }}"
|
||||
class="{% if request.endpoint == 'main.dashboard' %}active{% endif %}">
|
||||
Dashboard
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{{ url_for('settings.index') }}"
|
||||
class="{% if request.endpoint == 'settings.index' %}active{% endif %}">
|
||||
Settings
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<!-- Task Status Area -->
|
||||
<div id="task-status-area" style="display: none; margin-top: auto; padding: 10px; background-color: #161b22; border-top: 1px solid #30363d;">
|
||||
<div style="display: flex; align-items: center; margin-bottom: 8px;">
|
||||
<h3 style="margin: 0; font-size: 14px; color: #f0f6fc;">Current Tasks</h3>
|
||||
<div style="margin-left: auto;">
|
||||
<button id="close-tasks-btn" class="btn btn-sm" style="padding: 2px 8px;">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="tasks-container"></div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- Left sidebar navigation -->
|
||||
<nav class="sidebar">
|
||||
<ul class="sidebar-nav">
|
||||
<li><a href="{{ url_for('main.index') }}">Home</a></li>
|
||||
<li><a href="{{ url_for('podcasts.index') }}">Podcasts</a></li>
|
||||
<li><a href="{{ url_for('main.dashboard') }}">Dashboard</a></li>
|
||||
<li><a href="{{ url_for('settings.index') }}">Settings</a></li>
|
||||
<li><a href="{{ url_for('main.about') }}">About</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
<!-- Main content area -->
|
||||
<main class="main-content">
|
||||
<!-- Main Area -->
|
||||
<div class="main-area">
|
||||
<!-- Top Header -->
|
||||
<header class="top-header">
|
||||
<div class="header-search">
|
||||
<form action="{{ url_for('podcasts.search') }}" method="post">
|
||||
<input type="text" name="query" placeholder="Search podcasts..." value="{{ request.form.get('query', '') }}">
|
||||
</form>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- Flash Messages -->
|
||||
{% with messages = get_flashed_messages(with_categories=true) %}
|
||||
{% if messages %}
|
||||
<div class="flash-messages">
|
||||
|
@ -46,15 +83,199 @@
|
|||
</div>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
|
||||
|
||||
{% block content %}{% endblock %}
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer>
|
||||
<p>© 2023 Podcastrr</p>
|
||||
</footer>
|
||||
|
||||
{% block scripts %}{% endblock %}
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Task status polling
|
||||
let taskPollingInterval;
|
||||
let activeTasks = {};
|
||||
let needsRefresh = false;
|
||||
let seenTasks = JSON.parse(localStorage.getItem('seenTasks') || '{}');
|
||||
|
||||
// Elements
|
||||
const taskStatusArea = document.getElementById('task-status-area');
|
||||
const tasksContainer = document.getElementById('tasks-container');
|
||||
const closeTasksBtn = document.getElementById('close-tasks-btn');
|
||||
|
||||
// Close tasks panel
|
||||
closeTasksBtn.addEventListener('click', function() {
|
||||
taskStatusArea.style.display = 'none';
|
||||
|
||||
// Stop polling when the panel is closed
|
||||
clearInterval(taskPollingInterval);
|
||||
|
||||
// Clean up tasks on the server
|
||||
fetch('/api/tasks/clean', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({ max_age_seconds: 0 }) // Clean all completed/failed tasks
|
||||
});
|
||||
});
|
||||
|
||||
// Clean up old seen tasks (older than 1 day)
|
||||
cleanupSeenTasks();
|
||||
|
||||
// Start polling for tasks
|
||||
startTaskPolling();
|
||||
|
||||
function startTaskPolling() {
|
||||
// Initial fetch
|
||||
fetchTasks();
|
||||
|
||||
// Set up polling interval (every 2 seconds)
|
||||
taskPollingInterval = setInterval(fetchTasks, 2000);
|
||||
}
|
||||
|
||||
function fetchTasks() {
|
||||
fetch('/api/tasks')
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
updateTasksUI(data.tasks);
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error fetching tasks:', error);
|
||||
});
|
||||
}
|
||||
|
||||
function updateTasksUI(tasks) {
|
||||
// Filter for active tasks (pending or running)
|
||||
const runningTasks = tasks.filter(task =>
|
||||
task.status === 'pending' || task.status === 'running'
|
||||
);
|
||||
|
||||
// Show task area if there are running tasks
|
||||
if (runningTasks.length > 0) {
|
||||
taskStatusArea.style.display = 'block';
|
||||
needsRefresh = true;
|
||||
}
|
||||
|
||||
// Update active tasks tracking
|
||||
activeTasks = {};
|
||||
runningTasks.forEach(task => {
|
||||
activeTasks[task.id] = true;
|
||||
});
|
||||
|
||||
// Clear container
|
||||
tasksContainer.innerHTML = '';
|
||||
|
||||
// Add task items
|
||||
runningTasks.forEach(task => {
|
||||
const taskElement = createTaskElement(task);
|
||||
tasksContainer.appendChild(taskElement);
|
||||
});
|
||||
|
||||
// If no running tasks but we have completed/failed tasks from this session, show them
|
||||
if (runningTasks.length === 0) {
|
||||
// Filter out tasks that have already been seen
|
||||
const recentTasks = tasks.filter(task =>
|
||||
(task.status === 'completed' || task.status === 'failed') &&
|
||||
new Date(task.completed_at) > new Date(Date.now() - 10000) && // Last 10 seconds
|
||||
!seenTasks[task.id] // Only show tasks that haven't been seen
|
||||
);
|
||||
|
||||
if (recentTasks.length > 0) {
|
||||
taskStatusArea.style.display = 'block';
|
||||
|
||||
// Mark these tasks as seen with current timestamp
|
||||
recentTasks.forEach(task => {
|
||||
seenTasks[task.id] = new Date().getTime();
|
||||
const taskElement = createTaskElement(task);
|
||||
tasksContainer.appendChild(taskElement);
|
||||
});
|
||||
|
||||
// Save seen tasks to localStorage
|
||||
localStorage.setItem('seenTasks', JSON.stringify(seenTasks));
|
||||
|
||||
// Auto-hide after 5 seconds
|
||||
setTimeout(() => {
|
||||
taskStatusArea.style.display = 'none';
|
||||
}, 5000);
|
||||
|
||||
// If we had running tasks before and now they're complete, refresh the content
|
||||
// but only if we're on a page that would be affected by the task completion
|
||||
if (needsRefresh && window.location.pathname.includes('/podcasts/')) {
|
||||
// Add a small refresh button instead of auto-refreshing
|
||||
const refreshBtn = document.createElement('button');
|
||||
refreshBtn.className = 'btn btn-sm btn-primary';
|
||||
refreshBtn.style.marginTop = '8px';
|
||||
refreshBtn.style.width = '100%';
|
||||
refreshBtn.textContent = 'Refresh Content';
|
||||
refreshBtn.addEventListener('click', function() {
|
||||
// Only reload the content, not the entire page
|
||||
const contentArea = document.querySelector('.content-area');
|
||||
if (contentArea) {
|
||||
window.location.reload();
|
||||
}
|
||||
});
|
||||
tasksContainer.appendChild(refreshBtn);
|
||||
needsRefresh = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function cleanupSeenTasks() {
|
||||
// Get the current seenTasks from localStorage
|
||||
const storedTasks = JSON.parse(localStorage.getItem('seenTasks') || '{}');
|
||||
const now = new Date().getTime();
|
||||
const oneDayInMs = 24 * 60 * 60 * 1000; // 1 day in milliseconds
|
||||
const cleanedTasks = {};
|
||||
|
||||
// Only keep tasks that have timestamps and are less than 1 day old
|
||||
for (const taskId in storedTasks) {
|
||||
const timestamp = storedTasks[taskId];
|
||||
if (typeof timestamp === 'number' && now - timestamp < oneDayInMs) {
|
||||
cleanedTasks[taskId] = timestamp;
|
||||
} else if (storedTasks[taskId] === true) {
|
||||
// For tasks without timestamps (from previous version), add a timestamp now
|
||||
cleanedTasks[taskId] = now;
|
||||
}
|
||||
}
|
||||
|
||||
// Save the cleaned tasks back to localStorage
|
||||
localStorage.setItem('seenTasks', JSON.stringify(cleanedTasks));
|
||||
seenTasks = cleanedTasks;
|
||||
}
|
||||
|
||||
function createTaskElement(task) {
|
||||
const taskDiv = document.createElement('div');
|
||||
taskDiv.className = 'task-item';
|
||||
taskDiv.style.padding = '8px';
|
||||
taskDiv.style.marginBottom = '8px';
|
||||
taskDiv.style.backgroundColor = '#21262d';
|
||||
taskDiv.style.borderRadius = '4px';
|
||||
taskDiv.style.fontSize = '11px';
|
||||
|
||||
// Status color
|
||||
let statusColor = '#7d8590'; // Default gray
|
||||
if (task.status === 'running') statusColor = '#3fb950'; // Green
|
||||
if (task.status === 'completed') statusColor = '#3fb950'; // Green
|
||||
if (task.status === 'failed') statusColor = '#f85149'; // Red
|
||||
|
||||
// Create task content
|
||||
const taskContent = `
|
||||
<div style="display: flex; align-items: center; margin-bottom: 4px;">
|
||||
<span style="font-weight: bold; color: #f0f6fc;">${task.description}</span>
|
||||
<span style="margin-left: auto; color: ${statusColor}; font-size: 10px;">${task.status}</span>
|
||||
</div>
|
||||
<div style="margin-bottom: 6px; color: #7d8590; font-size: 10px;">${task.message || ''}</div>
|
||||
<div class="progress-bar" style="height: 4px; background-color: #30363d; border-radius: 2px; overflow: hidden;">
|
||||
<div class="progress-fill" style="height: 100%; width: ${task.progress}%; background-color: ${statusColor};"></div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
taskDiv.innerHTML = taskContent;
|
||||
return taskDiv;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue