diff --git a/Jellyfin.Plugin.SmartPlaylist/ScheduledTasks/GeneratePlaylist.cs b/Jellyfin.Plugin.SmartPlaylist/ScheduledTasks/GeneratePlaylist.cs index 4af2d60..174c3f8 100644 --- a/Jellyfin.Plugin.SmartPlaylist/ScheduledTasks/GeneratePlaylist.cs +++ b/Jellyfin.Plugin.SmartPlaylist/ScheduledTasks/GeneratePlaylist.cs @@ -95,25 +95,42 @@ namespace Jellyfin.Plugin.SmartPlaylist.ScheduledTasks { } private IEnumerable FilterPlaylistItems(IEnumerable items, User user, SmartPlaylistDto smartPlaylist) { - List results = new List(); - Expression expression = new Parser(StringTokenStream.generate(smartPlaylist.Program)).parse(); + List results = new List(); + Expression expression = new Parser(StringTokenStream.generate(smartPlaylist.Program)).parse(); // parse here, so that we don't repeat the work for each item Executor executor = new Executor(new DefaultEnvironment()); - executor.environment.Set("user", new Lisp_Object(user)); + executor.environment.Set("user", Lisp_Object.FromBase(user)); if (Plugin.Instance is not null) { executor.eval(Plugin.Instance.Configuration.InitialProgram); } else { throw new ApplicationException("Plugin Instance is not yet initialized"); } foreach (var i in items) { - executor.environment.Set("item", new Lisp_Object(i)); + executor.environment.Set("item", Lisp_Object.FromBase(i)); var r = executor.eval(expression); _logger.LogTrace("Item {0} evaluated to {1}", i, r.ToString()); if ((r is not Lisp_Boolean r_bool) || (r_bool.Value())) { _logger.LogDebug("Added '{0}' to Smart Playlist {1}", i, smartPlaylist.Name); - results.Add(i.Id); + results.Add(i); } } - return results; + executor = new Executor(new DefaultEnvironment()); + executor.environment.Set("user", Lisp_Object.FromBase(user)); + executor.environment.Set("items", Lisp_Object.FromBase(results)); + results = new List(); + var sort_result = executor.eval(smartPlaylist.SortProgram); + if (sort_result is Cons sorted_items) { + foreach (var i in sorted_items.ToList()) { + if (i is Lisp_Object iObject && iObject.Value() is BaseItem iBaseItem) { + results.Add(iBaseItem); + continue; + } + throw new ApplicationException($"Returned sorted list does contain unexpected items, got {i}"); + } + } else if (sort_result == Lisp_Boolean.FALSE) { + } else { + throw new ApplicationException($"Did not return a list of items, returned {sort_result}"); + } + return results.Select(x => x.Id); } private IEnumerable GetAllUserMedia(User user) { diff --git a/Jellyfin.Plugin.SmartPlaylist/SmartPlaylistDto.cs b/Jellyfin.Plugin.SmartPlaylist/SmartPlaylistDto.cs index 75bd102..7d2e05a 100644 --- a/Jellyfin.Plugin.SmartPlaylist/SmartPlaylistDto.cs +++ b/Jellyfin.Plugin.SmartPlaylist/SmartPlaylistDto.cs @@ -42,10 +42,12 @@ namespace Jellyfin.Plugin.SmartPlaylist { [Serializable] public class SmartPlaylistDto : ISerializable { private static string DEFAULT_PROGRAM = "(begin (invoke item \"IsFavoriteOrLiked\" (list user)))"; + private static string DEFAULT_SORT_PROGRAM = "(begin items)"; public SmartPlaylistId Id { get; set; } public SmartPlaylistLinkDto[] Playlists { get; set; } public string Name { get; set; } public string Program { get; set; } + public string SortProgram { get; set; } public string? Filename { get; set; } public bool Enabled { get; set; } @@ -54,6 +56,7 @@ namespace Jellyfin.Plugin.SmartPlaylist { Playlists = []; Name = Id.ToString(); Program = DEFAULT_PROGRAM; + SortProgram = DEFAULT_SORT_PROGRAM; Filename = null; Enabled = true; } @@ -79,6 +82,11 @@ namespace Jellyfin.Plugin.SmartPlaylist { } else { Program = DEFAULT_PROGRAM; } + if (info.GetValue("SortProgram", typeof(string)) is string _SortProgram) { + SortProgram = _SortProgram; + } else { + SortProgram = DEFAULT_SORT_PROGRAM; + } if (info.GetValue("Filename", typeof(string)) is string _Filename) { Filename = _Filename; } else {