diff --git a/TuneRipper.Api/Services/ISessionService.cs b/TuneRipper.Api/Services/ISessionService.cs
index 4f92364..1695b04 100644
--- a/TuneRipper.Api/Services/ISessionService.cs
+++ b/TuneRipper.Api/Services/ISessionService.cs
@@ -7,4 +7,5 @@ public interface ISessionService
bool IsValidSession(string sessionId);
void CleanupExpiredSessions();
string GetSessionDownloadsPath(string sessionId);
+ void EnsureSessionDirectoryExists(string sessionId);
}
\ No newline at end of file
diff --git a/TuneRipper.Api/Services/SessionService.cs b/TuneRipper.Api/Services/SessionService.cs
index 84257d0..6a99063 100644
--- a/TuneRipper.Api/Services/SessionService.cs
+++ b/TuneRipper.Api/Services/SessionService.cs
@@ -26,15 +26,18 @@ public class SessionService : ISessionService
);
}
+ ///
+ /// Creates a new session with a unique ID.
+ /// Note: The session directory is NOT created at this point - it will be created
+ /// lazily when the first MP3 file is saved using EnsureSessionDirectoryExists().
+ ///
+ /// The unique session ID
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);
-
+ // Don't create directory here - it will be created when first MP3 is saved
_logger.LogInformation("Created new session: {SessionId}", sessionId);
return sessionId;
}
@@ -83,7 +86,7 @@ public class SessionService : ISessionService
{
if (_sessions.TryRemove(sessionId, out _))
{
- // Clean up session directory
+ // Clean up session directory if it exists
var sessionPath = GetSessionDownloadsPath(sessionId);
if (Directory.Exists(sessionPath))
{
@@ -97,6 +100,10 @@ public class SessionService : ISessionService
_logger.LogWarning(ex, "Failed to delete expired session directory: {SessionId}", sessionId);
}
}
+ else
+ {
+ _logger.LogDebug("No directory to clean up for expired session: {SessionId} (no MP3 files were downloaded)", sessionId);
+ }
}
}
@@ -117,6 +124,34 @@ public class SessionService : ISessionService
return Path.Combine(downloadsRoot, sessionId);
}
+ ///
+ /// Ensures that the session directory exists, creating it if necessary.
+ /// This method should be called just before saving the first MP3 file in a session.
+ ///
+ /// The session ID
+ /// Thrown when sessionId is null or empty
+ /// Thrown when session is invalid or expired
+ public void EnsureSessionDirectoryExists(string sessionId)
+ {
+ if (string.IsNullOrWhiteSpace(sessionId))
+ {
+ throw new ArgumentException("Session ID cannot be null or empty", nameof(sessionId));
+ }
+
+ if (!IsValidSession(sessionId))
+ {
+ throw new InvalidOperationException($"Invalid session ID: {sessionId}");
+ }
+
+ var sessionPath = GetSessionDownloadsPath(sessionId);
+
+ if (!Directory.Exists(sessionPath))
+ {
+ Directory.CreateDirectory(sessionPath);
+ _logger.LogInformation("Created session directory for session: {SessionId} at {Path}", sessionId, sessionPath);
+ }
+ }
+
public void Dispose()
{
_cleanupTimer?.Dispose();
diff --git a/TuneRipper.Api/Services/VideoService.cs b/TuneRipper.Api/Services/VideoService.cs
index caba0ce..a2dd0a5 100644
--- a/TuneRipper.Api/Services/VideoService.cs
+++ b/TuneRipper.Api/Services/VideoService.cs
@@ -104,9 +104,11 @@ public class VideoService : IVideoService
throw new FileNotFoundException($"yt-dlp.exe not found at {ytDlpPath}");
}
- // Get session-specific download directory
+ // Ensure session directory exists only when we're about to save the first MP3 file
+ _sessionService.EnsureSessionDirectoryExists(sessionId);
+
+ // Get session-specific download directory (now guaranteed to exist)
var downloadDir = _sessionService.GetSessionDownloadsPath(sessionId);
- Directory.CreateDirectory(downloadDir);
// Get video details first for better filename
var videoDetails = await GetVideoDetailsAsync(url);