Prepare Linux deployment with yt-dlp and persistent downloads

This commit is contained in:
Michal Vrba
2026-10-19 17:23:30 +02:00
parent 471010c536
commit a26c584462
15 changed files with 331 additions and 139 deletions
+64 -10
View File
@@ -1,21 +1,32 @@
using System.Collections.Concurrent;
using Microsoft.Extensions.Options;
using TuneRipper.Api.Configuration;
namespace TuneRipper.Api.Services;
public class SessionService : ISessionService
public class SessionService : ISessionService, IDisposable
{
private readonly IWebHostEnvironment _environment;
private readonly ILogger<SessionService> _logger;
private readonly string _downloadsRoot;
private readonly ConcurrentDictionary<string, DateTime> _sessions = new();
private readonly Timer _cleanupTimer;
// Session expires after 24 hours of inactivity
private readonly TimeSpan _sessionTimeout = TimeSpan.FromHours(24);
public SessionService(IWebHostEnvironment environment, ILogger<SessionService> logger)
public SessionService(
IWebHostEnvironment environment,
IOptions<StorageOptions> options,
ILogger<SessionService> logger)
{
_environment = environment;
_logger = logger;
_downloadsRoot = Path.GetFullPath(
string.IsNullOrWhiteSpace(options.Value.DownloadsPath)
? Path.Combine(environment.WebRootPath, "downloads")
: options.Value.DownloadsPath);
Directory.CreateDirectory(_downloadsRoot);
RestoreSessionsFromDisk();
// Clean up expired sessions every hour
_cleanupTimer = new Timer(
@@ -46,8 +57,7 @@ public class SessionService : ISessionService
{
if (!string.IsNullOrWhiteSpace(sessionId) && IsValidSession(sessionId))
{
// Update last activity time
_sessions[sessionId] = DateTime.UtcNow;
TouchSession(sessionId);
return sessionId;
}
@@ -59,7 +69,17 @@ public class SessionService : ISessionService
if (string.IsNullOrWhiteSpace(sessionId))
return false;
if (_sessions.TryGetValue(sessionId, out var lastActivity))
if (!_sessions.TryGetValue(sessionId, out var lastActivity))
{
var sessionPath = GetSessionDownloadsPath(sessionId);
if (Directory.Exists(sessionPath))
{
lastActivity = Directory.GetLastWriteTimeUtc(sessionPath);
_sessions.TryAdd(sessionId, lastActivity);
}
}
if (_sessions.TryGetValue(sessionId, out lastActivity))
{
// Check if session has expired
if (DateTime.UtcNow - lastActivity > _sessionTimeout)
@@ -118,10 +138,11 @@ public class SessionService : ISessionService
}
}
public string GetDownloadsRootPath() => _downloadsRoot;
public string GetSessionDownloadsPath(string sessionId)
{
var downloadsRoot = Path.Combine(_environment.WebRootPath, "downloads");
return Path.Combine(downloadsRoot, sessionId);
return Path.Combine(_downloadsRoot, sessionId);
}
/// <summary>
@@ -150,10 +171,43 @@ public class SessionService : ISessionService
Directory.CreateDirectory(sessionPath);
_logger.LogInformation("Created session directory for session: {SessionId} at {Path}", sessionId, sessionPath);
}
TouchSession(sessionId);
}
private void RestoreSessionsFromDisk()
{
foreach (var sessionPath in Directory.EnumerateDirectories(_downloadsRoot))
{
var sessionId = Path.GetFileName(sessionPath);
var lastActivity = Directory.GetLastWriteTimeUtc(sessionPath);
if (DateTime.UtcNow - lastActivity <= _sessionTimeout)
{
_sessions[sessionId] = lastActivity;
}
}
_logger.LogInformation(
"Restored {Count} active sessions from {DownloadsRoot}",
_sessions.Count,
_downloadsRoot);
}
private void TouchSession(string sessionId)
{
var now = DateTime.UtcNow;
_sessions[sessionId] = now;
var sessionPath = GetSessionDownloadsPath(sessionId);
if (Directory.Exists(sessionPath))
{
Directory.SetLastWriteTimeUtc(sessionPath, now);
}
}
public void Dispose()
{
_cleanupTimer?.Dispose();
}
}
}