diff --git a/plugins/jellyfin/Jellyfin.Plugin.MovieNight/Configuration/ui.js b/plugins/jellyfin/Jellyfin.Plugin.MovieNight/Configuration/ui.js
index 3f48df3..4786f13 100644
--- a/plugins/jellyfin/Jellyfin.Plugin.MovieNight/Configuration/ui.js
+++ b/plugins/jellyfin/Jellyfin.Plugin.MovieNight/Configuration/ui.js
@@ -185,12 +185,22 @@
const footer = dialog.querySelector('.dialog-footer');
content.innerHTML = `
-
-
+
+
+
-
+
`;
@@ -206,10 +216,12 @@
btnAdd.onclick = async () => {
const title = dialog.querySelector('.txtTitle').value;
+ const year = dialog.querySelector('.txtYear').value;
+ const imdbId = dialog.querySelector('.txtImdb').value;
const url = dialog.querySelector('.txtUrl').value;
if (!title) return;
cleanup();
- await addMovie(title, url);
+ await addMovie(title, url, year, imdbId);
};
dialog.querySelector('.btnCancel').onclick = cleanup;
@@ -241,12 +253,16 @@
}
}
- async function addMovie(title, url) {
+ async function addMovie(title, url, year, imdbId) {
try {
+ const data = { title, url };
+ if (year) data.year = parseInt(year);
+ if (imdbId) data.imdbId = imdbId;
+
await ApiClient.ajax({
type: 'POST',
url: ApiClient.getUrl(`MovieNight/Films`),
- data: JSON.stringify({ title, url }),
+ data: JSON.stringify(data),
contentType: 'application/json'
});
showMsg(`STRM file created for "${title}". Refresh your library to see it.`);
diff --git a/plugins/jellyfin/Jellyfin.Plugin.MovieNight/Controllers/MovieNightController.cs b/plugins/jellyfin/Jellyfin.Plugin.MovieNight/Controllers/MovieNightController.cs
index 7cc1781..58b51ac 100644
--- a/plugins/jellyfin/Jellyfin.Plugin.MovieNight/Controllers/MovieNightController.cs
+++ b/plugins/jellyfin/Jellyfin.Plugin.MovieNight/Controllers/MovieNightController.cs
@@ -1,5 +1,7 @@
using System;
using System.IO;
+using System.Linq;
+using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Plugin.MovieNight.Services;
@@ -136,7 +138,8 @@ public class MovieNightController : ControllerBase
}
///
- /// Creates a new film by generating a .strm file.
+ /// Creates a new film by generating a .strm file in a folder-per-movie structure.
+ /// Structure: Movie Name (Year) [imdbid-ttXXXXXXX]/Movie Name (Year) [imdbid-ttXXXXXXX].strm
///
[HttpPost("Films")]
[Authorize]
@@ -148,24 +151,45 @@ public class MovieNightController : ControllerBase
return BadRequest("STRM output path is not configured.");
}
+ if (string.IsNullOrWhiteSpace(request.Title))
+ {
+ return BadRequest("Movie title is required.");
+ }
+
try
{
- if (!Directory.Exists(config.StrmOutputPath))
+ // Construct name: "Movie Name (Year) [imdbid-ttXXXXXXX]"
+ var folderName = request.Title.Trim();
+ if (request.Year.HasValue)
{
- Directory.CreateDirectory(config.StrmOutputPath);
+ folderName += $" ({request.Year})";
+ }
+ if (!string.IsNullOrWhiteSpace(request.ImdbId))
+ {
+ var ttId = request.ImdbId.Trim().ToLowerInvariant();
+ if (!ttId.StartsWith("tt")) ttId = "tt" + ttId;
+ folderName += $" [imdbid-{ttId}]";
}
- var safeTitle = string.Join("_", request.Title.Split(Path.GetInvalidFileNameChars()));
- var fileName = $"{safeTitle}.strm";
- var filePath = Path.Combine(config.StrmOutputPath, fileName);
+ // Sanitize for file system
+ var invalidChars = Path.GetInvalidFileNameChars();
+ var safeFolderName = new string(folderName.Select(c => invalidChars.Contains(c) ? '_' : c).ToArray());
+
+ var movieDirectory = Path.Combine(config.StrmOutputPath, safeFolderName);
+ if (!Directory.Exists(movieDirectory))
+ {
+ Directory.CreateDirectory(movieDirectory);
+ }
+
+ var filePath = Path.Combine(movieDirectory, $"{safeFolderName}.strm");
var strmContent = string.IsNullOrWhiteSpace(request.Url)
? "http://placeholder.url/upload_me_later"
- : request.Url;
+ : request.Url.Trim();
await System.IO.File.WriteAllTextAsync(filePath, strmContent).ConfigureAwait(false);
- return Ok(new { FilePath = filePath });
+ return Ok(new { FilePath = filePath, FolderName = safeFolderName });
}
catch (Exception ex)
{
@@ -177,7 +201,7 @@ public class MovieNightController : ControllerBase
///
/// Create film request.
///
-public sealed record CreateFilmRequest(string Title, string? Url);
+public sealed record CreateFilmRequest(string Title, string? Url, int? Year, string? ImdbId);
///
/// Rating request.