Organize downloaded files into per-job subdirectories under "downloads" instead of a flat structure. Update DownloadController and VideoService to read, write, and delete files within these job-specific directories. Add helper methods for directory management and improve logging. Update download/delete endpoints and result URLs to reflect the new structure. Clean up empty job directories after file deletion.
124 lines
3.9 KiB
C#
124 lines
3.9 KiB
C#
using System.Collections.Concurrent;
|
|
|
|
namespace TuneRipper.Api.Services;
|
|
|
|
public class SessionService : ISessionService
|
|
{
|
|
private readonly IWebHostEnvironment _environment;
|
|
private readonly ILogger<SessionService> _logger;
|
|
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)
|
|
{
|
|
_environment = environment;
|
|
_logger = logger;
|
|
|
|
// Clean up expired sessions every hour
|
|
_cleanupTimer = new Timer(
|
|
callback: _ => CleanupExpiredSessions(),
|
|
state: null,
|
|
dueTime: TimeSpan.FromHours(1),
|
|
period: TimeSpan.FromHours(1)
|
|
);
|
|
}
|
|
|
|
public string CreateSession()
|
|
{
|
|
var sessionId = Guid.NewGuid().ToString("N")[..16]; // Use first 16 characters for shorter IDs
|
|
_sessions[sessionId] = DateTime.UtcNow;
|
|
|
|
// Ensure session directory exists
|
|
var sessionPath = GetSessionDownloadsPath(sessionId);
|
|
Directory.CreateDirectory(sessionPath);
|
|
|
|
_logger.LogInformation("Created new session: {SessionId}", sessionId);
|
|
return sessionId;
|
|
}
|
|
|
|
public string GetOrCreateSession(string? sessionId)
|
|
{
|
|
if (!string.IsNullOrWhiteSpace(sessionId) && IsValidSession(sessionId))
|
|
{
|
|
// Update last activity time
|
|
_sessions[sessionId] = DateTime.UtcNow;
|
|
return sessionId;
|
|
}
|
|
|
|
return CreateSession();
|
|
}
|
|
|
|
public bool IsValidSession(string sessionId)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(sessionId))
|
|
return false;
|
|
|
|
if (_sessions.TryGetValue(sessionId, out var lastActivity))
|
|
{
|
|
// Check if session has expired
|
|
if (DateTime.UtcNow - lastActivity > _sessionTimeout)
|
|
{
|
|
_sessions.TryRemove(sessionId, out _);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public void CleanupExpiredSessions()
|
|
{
|
|
try
|
|
{
|
|
var expiredSessions = _sessions
|
|
.Where(kvp => DateTime.UtcNow - kvp.Value > _sessionTimeout)
|
|
.Select(kvp => kvp.Key)
|
|
.ToList();
|
|
|
|
foreach (var sessionId in expiredSessions)
|
|
{
|
|
if (_sessions.TryRemove(sessionId, out _))
|
|
{
|
|
// Clean up session directory
|
|
var sessionPath = GetSessionDownloadsPath(sessionId);
|
|
if (Directory.Exists(sessionPath))
|
|
{
|
|
try
|
|
{
|
|
Directory.Delete(sessionPath, recursive: true);
|
|
_logger.LogInformation("Cleaned up expired session directory: {SessionId}", sessionId);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogWarning(ex, "Failed to delete expired session directory: {SessionId}", sessionId);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (expiredSessions.Count > 0)
|
|
{
|
|
_logger.LogInformation("Cleaned up {Count} expired sessions", expiredSessions.Count);
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Error during session cleanup");
|
|
}
|
|
}
|
|
|
|
public string GetSessionDownloadsPath(string sessionId)
|
|
{
|
|
var downloadsRoot = Path.Combine(_environment.WebRootPath, "downloads");
|
|
return Path.Combine(downloadsRoot, sessionId);
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
_cleanupTimer?.Dispose();
|
|
}
|
|
} |