Refactor downloads to use job-specific subdirectories
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.
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using TuneRipper.Api.Services;
|
||||
|
||||
namespace TuneRipper.Api.Controllers;
|
||||
|
||||
@@ -8,40 +9,48 @@ public class DownloadController : ControllerBase
|
||||
{
|
||||
private readonly IWebHostEnvironment _environment;
|
||||
private readonly ILogger<DownloadController> _logger;
|
||||
private readonly ISessionService _sessionService;
|
||||
|
||||
public DownloadController(IWebHostEnvironment environment, ILogger<DownloadController> logger)
|
||||
public DownloadController(IWebHostEnvironment environment, ILogger<DownloadController> logger, ISessionService sessionService)
|
||||
{
|
||||
_environment = environment;
|
||||
_logger = logger;
|
||||
_sessionService = sessionService;
|
||||
}
|
||||
|
||||
[HttpGet("{jobId}")]
|
||||
public IActionResult DownloadFile(string jobId)
|
||||
// New session-aware download endpoint
|
||||
[HttpGet("{sessionId}/{jobId}")]
|
||||
public IActionResult DownloadFileBySession(string sessionId, string jobId)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(jobId))
|
||||
if (string.IsNullOrWhiteSpace(sessionId) || string.IsNullOrWhiteSpace(jobId))
|
||||
{
|
||||
return BadRequest("Job ID is required");
|
||||
return BadRequest("Session ID and Job ID are required");
|
||||
}
|
||||
|
||||
if (!_sessionService.IsValidSession(sessionId))
|
||||
{
|
||||
return NotFound("Invalid or expired session");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var downloadsDir = Path.Combine(_environment.WebRootPath, "downloads");
|
||||
var sessionDownloadsDir = _sessionService.GetSessionDownloadsPath(sessionId);
|
||||
|
||||
if (!Directory.Exists(downloadsDir))
|
||||
if (!Directory.Exists(sessionDownloadsDir))
|
||||
{
|
||||
_logger.LogWarning("Downloads directory not found: {Path}", downloadsDir);
|
||||
return NotFound("Downloads directory not found");
|
||||
_logger.LogWarning("Session downloads directory not found: {Path}", sessionDownloadsDir);
|
||||
return NotFound("Session downloads directory not found");
|
||||
}
|
||||
|
||||
// Find files with the specific job ID pattern: jobId_title.ext
|
||||
var jobFiles = Directory.GetFiles(downloadsDir, $"{jobId}_*.*")
|
||||
// Find files with the specific job ID pattern in the session directory
|
||||
var jobFiles = Directory.GetFiles(sessionDownloadsDir, $"{jobId}_*.*")
|
||||
.Where(f => !f.EndsWith(".part") && !f.EndsWith(".tmp"))
|
||||
.OrderByDescending(System.IO.File.GetCreationTime)
|
||||
.ToArray();
|
||||
|
||||
if (jobFiles.Length == 0)
|
||||
{
|
||||
_logger.LogWarning("No files found for job ID: {JobId}", jobId);
|
||||
_logger.LogWarning("No files found for job ID: {JobId} in session: {SessionId}", jobId, sessionId);
|
||||
return NotFound("File not found for the specified job ID");
|
||||
}
|
||||
|
||||
@@ -54,7 +63,7 @@ public class DownloadController : ControllerBase
|
||||
return NotFound("File not found");
|
||||
}
|
||||
|
||||
_logger.LogInformation("Serving file: {FileName} for job: {JobId}", fileName, jobId);
|
||||
_logger.LogInformation("Serving file: {FileName} for job: {JobId} in session: {SessionId}", fileName, jobId, sessionId);
|
||||
|
||||
// Determine content type based on file extension
|
||||
var contentType = Path.GetExtension(fileName).ToLowerInvariant() switch
|
||||
@@ -74,12 +83,99 @@ public class DownloadController : ControllerBase
|
||||
return File(fileStream, contentType, downloadFileName);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error downloading file for job ID: {JobId} in session: {SessionId}", jobId, sessionId);
|
||||
return StatusCode(500, $"Error downloading file: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
// Legacy endpoint for backward compatibility
|
||||
[HttpGet("{jobId}")]
|
||||
public IActionResult DownloadFile(string jobId)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(jobId))
|
||||
{
|
||||
return BadRequest("Job ID is required");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Check in the legacy downloads directory first
|
||||
var downloadsDir = Path.Combine(_environment.WebRootPath, "downloads");
|
||||
|
||||
if (Directory.Exists(downloadsDir))
|
||||
{
|
||||
var legacyFiles = Directory.GetFiles(downloadsDir, $"{jobId}_*.*")
|
||||
.Where(f => !f.EndsWith(".part") && !f.EndsWith(".tmp"))
|
||||
.OrderByDescending(System.IO.File.GetCreationTime)
|
||||
.ToArray();
|
||||
|
||||
if (legacyFiles.Length > 0)
|
||||
{
|
||||
return ServeFile(legacyFiles[0], jobId, null);
|
||||
}
|
||||
}
|
||||
|
||||
// If not found in legacy location, search all session directories
|
||||
var sessionDirs = Directory.GetDirectories(downloadsDir, "*", SearchOption.TopDirectoryOnly);
|
||||
|
||||
foreach (var sessionDir in sessionDirs)
|
||||
{
|
||||
var sessionId = Path.GetFileName(sessionDir);
|
||||
if (!_sessionService.IsValidSession(sessionId)) continue;
|
||||
|
||||
var jobFiles = Directory.GetFiles(sessionDir, $"{jobId}_*.*")
|
||||
.Where(f => !f.EndsWith(".part") && !f.EndsWith(".tmp"))
|
||||
.OrderByDescending(System.IO.File.GetCreationTime)
|
||||
.ToArray();
|
||||
|
||||
if (jobFiles.Length > 0)
|
||||
{
|
||||
return ServeFile(jobFiles[0], jobId, sessionId);
|
||||
}
|
||||
}
|
||||
|
||||
_logger.LogWarning("No files found for job ID: {JobId}", jobId);
|
||||
return NotFound("File not found for the specified job ID");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error downloading file for job ID: {JobId}", jobId);
|
||||
return StatusCode(500, $"Error downloading file: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private IActionResult ServeFile(string filePath, string jobId, string? sessionId)
|
||||
{
|
||||
var fileName = Path.GetFileName(filePath);
|
||||
|
||||
if (!System.IO.File.Exists(filePath))
|
||||
{
|
||||
_logger.LogWarning("File not found: {FilePath}", filePath);
|
||||
return NotFound("File not found");
|
||||
}
|
||||
|
||||
var sessionInfo = sessionId != null ? $" in session: {sessionId}" : "";
|
||||
_logger.LogInformation("Serving file: {FileName} for job: {JobId}{SessionInfo}", fileName, jobId, sessionInfo);
|
||||
|
||||
// Determine content type based on file extension
|
||||
var contentType = Path.GetExtension(fileName).ToLowerInvariant() switch
|
||||
{
|
||||
".mp3" => "audio/mpeg",
|
||||
".m4a" => "audio/mp4",
|
||||
".webm" => "audio/webm",
|
||||
".wav" => "audio/wav",
|
||||
".ogg" => "audio/ogg",
|
||||
_ => "application/octet-stream"
|
||||
};
|
||||
|
||||
// Create a user-friendly download filename from our pattern
|
||||
var downloadFileName = CreateUserFriendlyFileName(fileName);
|
||||
|
||||
var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
|
||||
return File(fileStream, contentType, downloadFileName);
|
||||
}
|
||||
|
||||
private string CreateUserFriendlyFileName(string originalFileName)
|
||||
{
|
||||
try
|
||||
@@ -118,25 +214,30 @@ public class DownloadController : ControllerBase
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet("info/{jobId}")]
|
||||
public IActionResult GetDownloadInfo(string jobId)
|
||||
[HttpGet("info/{sessionId}/{jobId}")]
|
||||
public IActionResult GetDownloadInfoBySession(string sessionId, string jobId)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(jobId))
|
||||
if (string.IsNullOrWhiteSpace(sessionId) || string.IsNullOrWhiteSpace(jobId))
|
||||
{
|
||||
return BadRequest("Job ID is required");
|
||||
return BadRequest("Session ID and Job ID are required");
|
||||
}
|
||||
|
||||
if (!_sessionService.IsValidSession(sessionId))
|
||||
{
|
||||
return NotFound("Invalid or expired session");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var downloadsDir = Path.Combine(_environment.WebRootPath, "downloads");
|
||||
var sessionDownloadsDir = _sessionService.GetSessionDownloadsPath(sessionId);
|
||||
|
||||
if (!Directory.Exists(downloadsDir))
|
||||
if (!Directory.Exists(sessionDownloadsDir))
|
||||
{
|
||||
return NotFound("Downloads directory not found");
|
||||
return NotFound("Session downloads directory not found");
|
||||
}
|
||||
|
||||
// Find files with the specific job ID pattern
|
||||
var jobFiles = Directory.GetFiles(downloadsDir, $"{jobId}_*.*")
|
||||
// Find files with the specific job ID pattern in the session directory
|
||||
var jobFiles = Directory.GetFiles(sessionDownloadsDir, $"{jobId}_*.*")
|
||||
.Where(f => !f.EndsWith(".part") && !f.EndsWith(".tmp"))
|
||||
.OrderByDescending(System.IO.File.GetCreationTime)
|
||||
.ToArray();
|
||||
@@ -155,6 +256,7 @@ public class DownloadController : ControllerBase
|
||||
var info = new
|
||||
{
|
||||
jobId,
|
||||
sessionId,
|
||||
fileName,
|
||||
friendlyFileName,
|
||||
fileSize = FormatFileSize(fileInfo.Length),
|
||||
@@ -162,20 +264,21 @@ public class DownloadController : ControllerBase
|
||||
format = Path.GetExtension(fileName).TrimStart('.').ToUpperInvariant(),
|
||||
createdAt = fileInfo.CreationTime,
|
||||
lastModified = fileInfo.LastWriteTime,
|
||||
downloadUrl = $"/api/download/{jobId}"
|
||||
downloadUrl = $"/api/download/{sessionId}/{jobId}"
|
||||
};
|
||||
|
||||
return Ok(info);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error getting download info for job ID: {JobId}", jobId);
|
||||
_logger.LogError(ex, "Error getting download info for job ID: {JobId} in session: {SessionId}", jobId, sessionId);
|
||||
return StatusCode(500, $"Error getting download info: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
[HttpDelete("{jobId}")]
|
||||
public IActionResult DeleteFile(string jobId)
|
||||
// Legacy info endpoint for backward compatibility
|
||||
[HttpGet("info/{jobId}")]
|
||||
public IActionResult GetDownloadInfo(string jobId)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(jobId))
|
||||
{
|
||||
@@ -191,8 +294,96 @@ public class DownloadController : ControllerBase
|
||||
return NotFound("Downloads directory not found");
|
||||
}
|
||||
|
||||
// Find and delete files with the specific job ID pattern
|
||||
var jobFiles = Directory.GetFiles(downloadsDir, $"{jobId}_*.*")
|
||||
// Check legacy location first
|
||||
var legacyFiles = Directory.GetFiles(downloadsDir, $"{jobId}_*.*")
|
||||
.Where(f => !f.EndsWith(".part") && !f.EndsWith(".tmp"))
|
||||
.OrderByDescending(System.IO.File.GetCreationTime)
|
||||
.ToArray();
|
||||
|
||||
if (legacyFiles.Length > 0)
|
||||
{
|
||||
return GetFileInfo(legacyFiles[0], jobId, null);
|
||||
}
|
||||
|
||||
// Search session directories
|
||||
var sessionDirs = Directory.GetDirectories(downloadsDir, "*", SearchOption.TopDirectoryOnly);
|
||||
|
||||
foreach (var sessionDir in sessionDirs)
|
||||
{
|
||||
var sessionId = Path.GetFileName(sessionDir);
|
||||
if (!_sessionService.IsValidSession(sessionId)) continue;
|
||||
|
||||
var jobFiles = Directory.GetFiles(sessionDir, $"{jobId}_*.*")
|
||||
.Where(f => !f.EndsWith(".part") && !f.EndsWith(".tmp"))
|
||||
.OrderByDescending(System.IO.File.GetCreationTime)
|
||||
.ToArray();
|
||||
|
||||
if (jobFiles.Length > 0)
|
||||
{
|
||||
return GetFileInfo(jobFiles[0], jobId, sessionId);
|
||||
}
|
||||
}
|
||||
|
||||
return NotFound("File not found for the specified job ID");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error getting download info for job ID: {JobId}", jobId);
|
||||
return StatusCode(500, $"Error getting download info: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private IActionResult GetFileInfo(string filePath, string jobId, string? sessionId)
|
||||
{
|
||||
var fileName = Path.GetFileName(filePath);
|
||||
var fileInfo = new FileInfo(filePath);
|
||||
var friendlyFileName = CreateUserFriendlyFileName(fileName);
|
||||
|
||||
var downloadUrl = sessionId != null
|
||||
? $"/api/download/{sessionId}/{jobId}"
|
||||
: $"/api/download/{jobId}";
|
||||
|
||||
var info = new
|
||||
{
|
||||
jobId,
|
||||
sessionId,
|
||||
fileName,
|
||||
friendlyFileName,
|
||||
fileSize = FormatFileSize(fileInfo.Length),
|
||||
fileSizeBytes = fileInfo.Length,
|
||||
format = Path.GetExtension(fileName).TrimStart('.').ToUpperInvariant(),
|
||||
createdAt = fileInfo.CreationTime,
|
||||
lastModified = fileInfo.LastWriteTime,
|
||||
downloadUrl
|
||||
};
|
||||
|
||||
return Ok(info);
|
||||
}
|
||||
|
||||
[HttpDelete("{sessionId}/{jobId}")]
|
||||
public IActionResult DeleteFileBySession(string sessionId, string jobId)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(sessionId) || string.IsNullOrWhiteSpace(jobId))
|
||||
{
|
||||
return BadRequest("Session ID and Job ID are required");
|
||||
}
|
||||
|
||||
if (!_sessionService.IsValidSession(sessionId))
|
||||
{
|
||||
return NotFound("Invalid or expired session");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var sessionDownloadsDir = _sessionService.GetSessionDownloadsPath(sessionId);
|
||||
|
||||
if (!Directory.Exists(sessionDownloadsDir))
|
||||
{
|
||||
return NotFound("Session downloads directory not found");
|
||||
}
|
||||
|
||||
// Find and delete files with the specific job ID pattern in the session directory
|
||||
var jobFiles = Directory.GetFiles(sessionDownloadsDir, $"{jobId}_*.*")
|
||||
.ToArray();
|
||||
|
||||
var deletedCount = 0;
|
||||
@@ -203,7 +394,7 @@ public class DownloadController : ControllerBase
|
||||
{
|
||||
System.IO.File.Delete(filePath);
|
||||
deletedCount++;
|
||||
_logger.LogInformation("Deleted file: {FilePath} for job: {JobId}", filePath, jobId);
|
||||
_logger.LogInformation("Deleted file: {FilePath} for job: {JobId} in session: {SessionId}", filePath, jobId, sessionId);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -216,6 +407,78 @@ public class DownloadController : ControllerBase
|
||||
return NotFound("No files found to delete for the specified job ID");
|
||||
}
|
||||
|
||||
return Ok(new { message = $"Deleted {deletedCount} file(s) for job {jobId} in session {sessionId}", deletedCount, jobId, sessionId });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error deleting files for job ID: {JobId} in session: {SessionId}", jobId, sessionId);
|
||||
return StatusCode(500, $"Error deleting files: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
// Legacy delete endpoint for backward compatibility
|
||||
[HttpDelete("{jobId}")]
|
||||
public IActionResult DeleteFile(string jobId)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(jobId))
|
||||
{
|
||||
return BadRequest("Job ID is required");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var downloadsDir = Path.Combine(_environment.WebRootPath, "downloads");
|
||||
var deletedCount = 0;
|
||||
|
||||
if (!Directory.Exists(downloadsDir))
|
||||
{
|
||||
return NotFound("Downloads directory not found");
|
||||
}
|
||||
|
||||
// Delete from legacy location
|
||||
var legacyFiles = Directory.GetFiles(downloadsDir, $"{jobId}_*.*");
|
||||
foreach (var filePath in legacyFiles)
|
||||
{
|
||||
try
|
||||
{
|
||||
System.IO.File.Delete(filePath);
|
||||
deletedCount++;
|
||||
_logger.LogInformation("Deleted legacy file: {FilePath} for job: {JobId}", filePath, jobId);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning(ex, "Failed to delete legacy file: {FilePath}", filePath);
|
||||
}
|
||||
}
|
||||
|
||||
// Delete from session directories
|
||||
var sessionDirs = Directory.GetDirectories(downloadsDir, "*", SearchOption.TopDirectoryOnly);
|
||||
|
||||
foreach (var sessionDir in sessionDirs)
|
||||
{
|
||||
var sessionId = Path.GetFileName(sessionDir);
|
||||
var jobFiles = Directory.GetFiles(sessionDir, $"{jobId}_*.*");
|
||||
|
||||
foreach (var filePath in jobFiles)
|
||||
{
|
||||
try
|
||||
{
|
||||
System.IO.File.Delete(filePath);
|
||||
deletedCount++;
|
||||
_logger.LogInformation("Deleted file: {FilePath} for job: {JobId} in session: {SessionId}", filePath, jobId, sessionId);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning(ex, "Failed to delete file: {FilePath}", filePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (deletedCount == 0)
|
||||
{
|
||||
return NotFound("No files found to delete for the specified job ID");
|
||||
}
|
||||
|
||||
return Ok(new { message = $"Deleted {deletedCount} file(s) for job {jobId}", deletedCount, jobId });
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using TuneRipper.Api.Services;
|
||||
|
||||
namespace TuneRipper.Api.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Route("api/[controller]")]
|
||||
public class SessionController : ControllerBase
|
||||
{
|
||||
private readonly ISessionService _sessionService;
|
||||
private readonly ILogger<SessionController> _logger;
|
||||
|
||||
public SessionController(ISessionService sessionService, ILogger<SessionController> logger)
|
||||
{
|
||||
_sessionService = sessionService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
[HttpPost("create")]
|
||||
public ActionResult<object> CreateSession()
|
||||
{
|
||||
try
|
||||
{
|
||||
var sessionId = _sessionService.CreateSession();
|
||||
|
||||
return Ok(new
|
||||
{
|
||||
sessionId,
|
||||
message = "Session created successfully",
|
||||
expiresAt = DateTime.UtcNow.AddHours(24)
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error creating session");
|
||||
return StatusCode(500, "Error creating session");
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet("validate/{sessionId}")]
|
||||
public ActionResult<object> ValidateSession(string sessionId)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(sessionId))
|
||||
{
|
||||
return BadRequest("Session ID is required");
|
||||
}
|
||||
|
||||
var isValid = _sessionService.IsValidSession(sessionId);
|
||||
|
||||
return Ok(new
|
||||
{
|
||||
sessionId,
|
||||
isValid,
|
||||
message = isValid ? "Session is valid" : "Session is invalid or expired"
|
||||
});
|
||||
}
|
||||
|
||||
[HttpPost("refresh/{sessionId}")]
|
||||
public ActionResult<object> RefreshSession(string sessionId)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(sessionId))
|
||||
{
|
||||
return BadRequest("Session ID is required");
|
||||
}
|
||||
|
||||
if (!_sessionService.IsValidSession(sessionId))
|
||||
{
|
||||
return NotFound("Session not found or expired");
|
||||
}
|
||||
|
||||
// Session is automatically refreshed when IsValidSession is called
|
||||
return Ok(new
|
||||
{
|
||||
sessionId,
|
||||
message = "Session refreshed successfully",
|
||||
expiresAt = DateTime.UtcNow.AddHours(24)
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@ namespace TuneRipper.Api.Controllers;
|
||||
public class VideoController : ControllerBase
|
||||
{
|
||||
private readonly IVideoService _videoService;
|
||||
private readonly ISessionService _sessionService;
|
||||
private readonly ILogger<VideoController> _logger;
|
||||
|
||||
// In-memory storage for processing status (in production, use database or Redis)
|
||||
@@ -18,6 +19,7 @@ public class VideoController : ControllerBase
|
||||
private class ProcessingJob
|
||||
{
|
||||
public string Id { get; set; } = string.Empty;
|
||||
public string SessionId { get; set; } = string.Empty; // Add session tracking
|
||||
public ProcessVideoRequest Request { get; set; } = new();
|
||||
public DownloadProgress Progress { get; set; } = new();
|
||||
public ProcessVideoResponse? Result { get; set; }
|
||||
@@ -26,9 +28,10 @@ public class VideoController : ControllerBase
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
public VideoController(IVideoService videoService, ILogger<VideoController> logger)
|
||||
public VideoController(IVideoService videoService, ISessionService sessionService, ILogger<VideoController> logger)
|
||||
{
|
||||
_videoService = videoService;
|
||||
_sessionService = sessionService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
@@ -49,6 +52,9 @@ public class VideoController : ControllerBase
|
||||
{
|
||||
_logger.LogInformation("Starting video processing for URL: {Url}", request.Url);
|
||||
|
||||
// Get or create session
|
||||
var sessionId = _sessionService.GetOrCreateSession(request.SessionId);
|
||||
|
||||
// Generate a job ID
|
||||
var jobId = Guid.NewGuid().ToString();
|
||||
|
||||
@@ -56,6 +62,7 @@ public class VideoController : ControllerBase
|
||||
var job = new ProcessingJob
|
||||
{
|
||||
Id = jobId,
|
||||
SessionId = sessionId,
|
||||
Request = request,
|
||||
Progress = new DownloadProgress
|
||||
{
|
||||
@@ -76,7 +83,8 @@ public class VideoController : ControllerBase
|
||||
Message = "Processing started",
|
||||
FileName = jobId, // Use job ID as temporary filename
|
||||
DownloadUrl = null, // Will be available when processing completes
|
||||
VideoDetails = null // Will be populated during processing
|
||||
VideoDetails = null, // Will be populated during processing
|
||||
SessionId = sessionId // Return session ID to client
|
||||
};
|
||||
|
||||
return Accepted(response);
|
||||
@@ -140,10 +148,18 @@ public class VideoController : ControllerBase
|
||||
return Ok(job.Result);
|
||||
}
|
||||
|
||||
[HttpGet("jobs")]
|
||||
public ActionResult<IEnumerable<JobStatusResponse>> GetAllJobs()
|
||||
[HttpGet("jobs/{sessionId?}")]
|
||||
public ActionResult<IEnumerable<JobStatusResponse>> GetJobsBySession(string? sessionId = null)
|
||||
{
|
||||
var jobs = _processingJobs.Values.Select(job => new JobStatusResponse
|
||||
var jobsQuery = _processingJobs.Values.AsEnumerable();
|
||||
|
||||
// Filter by session if provided
|
||||
if (!string.IsNullOrWhiteSpace(sessionId))
|
||||
{
|
||||
jobsQuery = jobsQuery.Where(job => job.SessionId == sessionId);
|
||||
}
|
||||
|
||||
var jobs = jobsQuery.Select(job => new JobStatusResponse
|
||||
{
|
||||
JobId = job.Id,
|
||||
Status = job.Progress.Status,
|
||||
@@ -157,6 +173,13 @@ public class VideoController : ControllerBase
|
||||
return Ok(jobs);
|
||||
}
|
||||
|
||||
// Keep the old endpoint for backward compatibility
|
||||
[HttpGet("jobs")]
|
||||
public ActionResult<IEnumerable<JobStatusResponse>> GetAllJobs()
|
||||
{
|
||||
return GetJobsBySession(null);
|
||||
}
|
||||
|
||||
private async Task ProcessVideoAsync(string jobId)
|
||||
{
|
||||
if (!_processingJobs.TryGetValue(jobId, out var job))
|
||||
@@ -164,7 +187,7 @@ public class VideoController : ControllerBase
|
||||
|
||||
try
|
||||
{
|
||||
_logger.LogInformation("Processing video for job {JobId}", jobId);
|
||||
_logger.LogInformation("Processing video for job {JobId} in session {SessionId}", jobId, job.SessionId);
|
||||
|
||||
// Step 1: Fetch video details
|
||||
job.Progress.Status = "Processing";
|
||||
@@ -195,6 +218,7 @@ public class VideoController : ControllerBase
|
||||
job.Request.Url!,
|
||||
job.Request.Format ?? "mp3",
|
||||
jobId, // Pass the job ID for filename generation
|
||||
job.SessionId, // Pass the session ID for directory organization
|
||||
(percentage, message) =>
|
||||
{
|
||||
// Update progress from yt-dlp
|
||||
@@ -217,8 +241,8 @@ public class VideoController : ControllerBase
|
||||
job.Progress.Message = "Processing completed successfully";
|
||||
job.Progress.PercentageComplete = 100;
|
||||
|
||||
// Create download URL for the actual file
|
||||
var downloadUrl = $"/downloads/{Uri.EscapeDataString(downloadedFileName)}";
|
||||
// Create download URL for the actual file within session
|
||||
var downloadUrl = $"/api/download/{job.SessionId}/{jobId}";
|
||||
|
||||
job.Result = new ProcessVideoResponse
|
||||
{
|
||||
@@ -226,14 +250,15 @@ public class VideoController : ControllerBase
|
||||
Message = "Video processed successfully",
|
||||
FileName = downloadedFileName,
|
||||
DownloadUrl = downloadUrl,
|
||||
VideoDetails = videoDetails
|
||||
VideoDetails = videoDetails,
|
||||
SessionId = job.SessionId
|
||||
};
|
||||
|
||||
_logger.LogInformation("Successfully completed processing for job {JobId}. File: {FileName}", jobId, downloadedFileName);
|
||||
_logger.LogInformation("Successfully completed processing for job {JobId} in session {SessionId}. File: {FileName}", jobId, job.SessionId, downloadedFileName);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error processing video for job {JobId}", jobId);
|
||||
_logger.LogError(ex, "Error processing video for job {JobId} in session {SessionId}", jobId, job.SessionId);
|
||||
|
||||
job.Progress.Status = "Failed";
|
||||
job.Progress.Message = $"Processing failed: {ex.Message}";
|
||||
@@ -245,7 +270,8 @@ public class VideoController : ControllerBase
|
||||
Message = ex.Message,
|
||||
FileName = null,
|
||||
DownloadUrl = null,
|
||||
VideoDetails = job.VideoDetails // Include video details even on failure if we got them
|
||||
VideoDetails = job.VideoDetails, // Include video details even on failure if we got them
|
||||
SessionId = job.SessionId
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user