using Microsoft.Extensions.Logging; using Jellyfin.Data.Entities; using Jellyfin.Data.Enums; using MediaBrowser.Controller.Entities; using Jellyfin.Plugin.SmartPlaylist.Lisp; using Jellyfin.Plugin.SmartPlaylist.Lisp.Compiler; namespace Jellyfin.Plugin.SmartPlaylist.ScheduledTasks { public class Common { public static readonly BaseItemKind[] AvailableFilterItems = { BaseItemKind.Audio, BaseItemKind.MusicAlbum, BaseItemKind.Playlist, }; public readonly ILogger _logger; public Common(ILogger logger) { _logger = logger; } public Executor SetupExecutor() { var env = new DefaultEnvironment(); var executor = new Executor(env); executor.builtins["logd"] = (x) => { _logger.LogDebug(((Lisp.String)x.First()).Value(), x.Skip(1).ToArray()); return Lisp.Boolean.TRUE; }; executor.builtins["logi"] = (x) => { _logger.LogInformation(((Lisp.String)x.First()).Value(), x.Skip(1).ToArray()); return Lisp.Boolean.TRUE; }; executor.builtins["logw"] = (x) => { _logger.LogWarning(((Lisp.String)x.First()).Value(), x.Skip(1).ToArray()); return Lisp.Boolean.TRUE; }; executor.builtins["loge"] = (x) => { _logger.LogError(((Lisp.String)x.First()).Value(), x.Skip(1).ToArray()); return Lisp.Boolean.TRUE; }; if (Plugin.Instance is not null) { executor.eval(Plugin.Instance.Configuration.InitialProgram); } else { throw new ApplicationException("Plugin Instance is not yet initialized"); } return executor; } public IEnumerable FilterCollectionItems(IEnumerable items, User? user, string name, string program, string sortProgram) { List results = new List(); Expression expression = new Parser(StringTokenStream.generate(program)).parse(); // parse here, so that we don't repeat the work for each item Executor executor = SetupExecutor(); executor.environment.Set("*user*", Lisp.Object.FromBase(user)); foreach (var i in items) { 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 Collection {1}", i, name); results.Add(i); } } executor = SetupExecutor(); executor.environment.Set("*user*", Lisp.Object.FromBase(user)); executor.environment.Set("*items*", Lisp.Object.FromBase(results)); results = new List(); var sort_result = executor.eval(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); } } }