- Removed the global configuration form and redirected to the consolidated settings page. - Updated the dashboard to provide feedback when no streamers are configured and added edit links for each streamer. - Introduced a new media library page to display media files from the configured archive root. - Enhanced the streamer configuration page with additional options for overrides and settings, including a confirmation modal for deletion. - Updated the layout and styles for improved user experience and navigation. - Switched from file-based password storage to database-backed user credentials management in AuthService. - Applied EF migrations on application startup to ensure database schema is up-to-date.
96 lines
3 KiB
Text
96 lines
3 KiB
Text
@page "/"
|
|
@inject TwitchArchive.Core.Workers.StreamWorkerManager WorkerManager
|
|
@inject TwitchArchive.Web.Services.SessionCacheService SessionCache
|
|
|
|
<h2>Dashboard</h2>
|
|
|
|
@if (streamers.Count == 0)
|
|
{
|
|
<div class="alert alert-info">No streamers configured. Add one on the <a href="/addstreamer">Add Streamer</a> page.</div>
|
|
}
|
|
|
|
<div class="cards">
|
|
@foreach (var s in streamers)
|
|
{
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<a href="/streamer/@s">@s</a>
|
|
<a class="btn-link" href="/config/@s">Edit</a>
|
|
<span class="badge">@(WorkerManager.IsRunning(s) ? "Live" : "Offline")</span>
|
|
</div>
|
|
<div class="card-body">
|
|
<div>Last session: @(lastStarts.ContainsKey(s) ? lastStarts[s].ToLocalTime().ToString() : "-")</div>
|
|
<div class="actions">
|
|
<button @onclick="() => Start(s)">Start</button>
|
|
<button @onclick="() => Stop(s)">Stop</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
}
|
|
</div>
|
|
|
|
@* Show global feedback when there are streamers but no recent sessions *@
|
|
@if (streamers.Count > 0 && (lastStarts == null || lastStarts.Count == 0))
|
|
{
|
|
<div class="alert alert-warning mt-3">No recent sessions found for configured streamers.</div>
|
|
}
|
|
|
|
@code {
|
|
private List<string> streamers = new();
|
|
private Dictionary<string, DateTime> lastStarts = new();
|
|
|
|
private void OnCacheUpdatedHandler()
|
|
{
|
|
_ = InvokeAsync(() => {
|
|
lastStarts = SessionCache.GetSnapshot();
|
|
StateHasChanged();
|
|
});
|
|
}
|
|
|
|
protected override async Task OnInitializedAsync()
|
|
{
|
|
LoadStreamers();
|
|
lastStarts = SessionCache.GetSnapshot();
|
|
SessionCache.Updated += OnCacheUpdatedHandler;
|
|
}
|
|
|
|
private void LoadStreamers()
|
|
{
|
|
// Try to find the config/streamers folder from the app content root and parent folders.
|
|
string? cfgDir = FindConfigStreamersFolder();
|
|
if (!string.IsNullOrEmpty(cfgDir) && Directory.Exists(cfgDir))
|
|
{
|
|
streamers = Directory.GetFiles(cfgDir, "*.json").Select(f => Path.GetFileNameWithoutExtension(f)).ToList();
|
|
}
|
|
}
|
|
|
|
private string? FindConfigStreamersFolder()
|
|
{
|
|
// Prefer ContentRoot if available, fall back to Environment.CurrentDirectory.
|
|
var start = AppContext.BaseDirectory ?? Environment.CurrentDirectory;
|
|
var dir = new DirectoryInfo(start);
|
|
for (int i = 0; i < 6 && dir != null; i++)
|
|
{
|
|
var candidate = Path.Combine(dir.FullName, "config", "streamers");
|
|
if (Directory.Exists(candidate)) return candidate;
|
|
dir = dir.Parent;
|
|
}
|
|
|
|
// final attempt: repo-root relative (use project parent heuristics)
|
|
var alt = Path.Combine(Environment.CurrentDirectory, "..", "..", "..", "config", "streamers");
|
|
try { alt = Path.GetFullPath(alt); } catch { }
|
|
if (Directory.Exists(alt)) return alt;
|
|
return null;
|
|
}
|
|
|
|
// Index reads from the singleton SessionCacheService; updates are pushed via the Updated event.
|
|
|
|
private void Start(string u) { WorkerManager.StartWorker(u); }
|
|
private async Task Stop(string u) { await WorkerManager.StopWorkerAsync(u); }
|
|
|
|
public async ValueTask DisposeAsync()
|
|
{
|
|
SessionCache.Updated -= OnCacheUpdatedHandler;
|
|
await Task.CompletedTask;
|
|
}
|
|
}
|