Enhance overlay image handling: support dynamic filenames, add delete and rename functionality, and improve seasonal image management
This commit is contained in:
@@ -16,7 +16,6 @@ namespace Jellyfin.Plugin.MediaBarEnhanced.Api
|
||||
{
|
||||
private readonly IApplicationPaths _applicationPaths;
|
||||
private readonly string _imageDirectory;
|
||||
private readonly string _imagePath;
|
||||
|
||||
public OverlayImageController(IApplicationPaths applicationPaths)
|
||||
{
|
||||
@@ -25,8 +24,8 @@ namespace Jellyfin.Plugin.MediaBarEnhanced.Api
|
||||
// We use the plugin's data folder to store the image
|
||||
_imageDirectory = MediaBarEnhancedPlugin.Instance?.DataFolderPath ?? Path.Combine(applicationPaths.DataPath, "plugins", "MediaBarEnhanced");
|
||||
|
||||
// We'll just overwrite this single file each time
|
||||
_imagePath = Path.Combine(_imageDirectory, "custom_overlay_image.dat");
|
||||
// We no longer define the exact path here, just the directory
|
||||
// The filename is determined per request
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -34,13 +33,18 @@ namespace Jellyfin.Plugin.MediaBarEnhanced.Api
|
||||
/// </summary>
|
||||
[HttpPost("OverlayImage")]
|
||||
[Consumes("multipart/form-data")]
|
||||
public async Task<IActionResult> UploadImage([FromForm] IFormFile file)
|
||||
public async Task<IActionResult> UploadImage([FromForm] IFormFile file, [FromQuery] string? filename = null)
|
||||
{
|
||||
if (file == null || file.Length == 0)
|
||||
{
|
||||
return BadRequest("No file uploaded.");
|
||||
}
|
||||
|
||||
string targetFileName = string.IsNullOrWhiteSpace(filename)
|
||||
? "custom_overlay_image.dat"
|
||||
: $"custom_overlay_image_{filename}.dat";
|
||||
string targetPath = Path.Combine(_imageDirectory, targetFileName);
|
||||
|
||||
try
|
||||
{
|
||||
if (!Directory.Exists(_imageDirectory))
|
||||
@@ -51,13 +55,14 @@ namespace Jellyfin.Plugin.MediaBarEnhanced.Api
|
||||
// Delete is not strictly necessary and can cause locking issues if someone is currently reading it.
|
||||
// FileMode.Create will truncate the file if it exists, effectively overwriting it.
|
||||
// We use FileShare.None to ensure we have exclusive write access, but handle potential IOExceptions gracefully.
|
||||
using (var stream = new FileStream(_imagePath, FileMode.Create, FileAccess.Write, FileShare.None))
|
||||
using (var stream = new FileStream(targetPath, FileMode.Create, FileAccess.Write, FileShare.None))
|
||||
{
|
||||
await file.CopyToAsync(stream).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
// Return the GET URL that the frontend can use
|
||||
var getUrl = "/MediaBarEnhanced/OverlayImage?t=" + DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
|
||||
var qs = string.IsNullOrWhiteSpace(filename) ? "" : $"?filename={Uri.EscapeDataString(filename)}&";
|
||||
var getUrl = $"/MediaBarEnhanced/OverlayImage{qs}{(qs == "" ? "?" : "")}t={DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()}";
|
||||
return Ok(new { url = getUrl });
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -70,9 +75,14 @@ namespace Jellyfin.Plugin.MediaBarEnhanced.Api
|
||||
/// Retrieves the custom overlay image.
|
||||
/// </summary>
|
||||
[HttpGet("OverlayImage")]
|
||||
public IActionResult GetImage()
|
||||
public IActionResult GetImage([FromQuery] string? filename = null)
|
||||
{
|
||||
if (!System.IO.File.Exists(_imagePath))
|
||||
string targetFileName = string.IsNullOrWhiteSpace(filename)
|
||||
? "custom_overlay_image.dat"
|
||||
: $"custom_overlay_image_{filename}.dat";
|
||||
string targetPath = Path.Combine(_imageDirectory, targetFileName);
|
||||
|
||||
if (!System.IO.File.Exists(targetPath))
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
@@ -80,10 +90,78 @@ namespace Jellyfin.Plugin.MediaBarEnhanced.Api
|
||||
// Read the file and return as a generic octet stream.
|
||||
// We use FileShare.ReadWrite so that if someone is currently overwriting the file (uploading), we don't block them,
|
||||
// and we also don't get blocked by other readers.
|
||||
var stream = new FileStream(_imagePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
var stream = new FileStream(targetPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
|
||||
// "image/*" works reliably as browsers will sniff the exact image mime type (jpeg, png, webp).
|
||||
return File(stream, "image/*");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a custom overlay image.
|
||||
/// </summary>
|
||||
[HttpDelete("OverlayImage")]
|
||||
public IActionResult DeleteImage([FromQuery] string? filename = null)
|
||||
{
|
||||
string targetFileName = string.IsNullOrWhiteSpace(filename)
|
||||
? "custom_overlay_image.dat"
|
||||
: $"custom_overlay_image_{filename}.dat";
|
||||
string targetPath = Path.Combine(_imageDirectory, targetFileName);
|
||||
|
||||
if (System.IO.File.Exists(targetPath))
|
||||
{
|
||||
try
|
||||
{
|
||||
System.IO.File.Delete(targetPath);
|
||||
return Ok();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return StatusCode(500, $"Error deleting file: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renames a custom overlay image (used when a seasonal section is renamed).
|
||||
/// </summary>
|
||||
[HttpPut("OverlayImage/Rename")]
|
||||
public IActionResult RenameImage([FromQuery] string oldName, [FromQuery] string newName)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(oldName) || string.IsNullOrWhiteSpace(newName))
|
||||
{
|
||||
return BadRequest("Both oldName and newName must be provided.");
|
||||
}
|
||||
|
||||
string oldPath = Path.Combine(_imageDirectory, $"custom_overlay_image_{oldName}.dat");
|
||||
string newPath = Path.Combine(_imageDirectory, $"custom_overlay_image_{newName}.dat");
|
||||
|
||||
if (!System.IO.File.Exists(oldPath))
|
||||
{
|
||||
// If it doesn't exist, there is nothing to rename, but we still consider it a success
|
||||
// since the end state (file with oldName is gone, file with newName doesn't exist yet) is acceptable.
|
||||
return Ok();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// If a file with the new name already exists, delete it first to avoid conflicts
|
||||
if (System.IO.File.Exists(newPath))
|
||||
{
|
||||
System.IO.File.Delete(newPath);
|
||||
}
|
||||
|
||||
System.IO.File.Move(oldPath, newPath);
|
||||
|
||||
var qs = $"?filename={Uri.EscapeDataString(newName)}&";
|
||||
var getUrl = $"/MediaBarEnhanced/OverlayImage{qs}t={DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()}";
|
||||
return Ok(new { url = getUrl });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return StatusCode(500, $"Error renaming file: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user