diff --git a/Jellyfin.Plugin.SmartPlaylist/Api/LispPlaygroundController.cs b/Jellyfin.Plugin.SmartPlaylist/Api/LispPlaygroundController.cs new file mode 100644 index 0000000..2b4278e --- /dev/null +++ b/Jellyfin.Plugin.SmartPlaylist/Api/LispPlaygroundController.cs @@ -0,0 +1,101 @@ +using System.Net.Mime; +using System.Text; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using System.ComponentModel.DataAnnotations; +using MediaBrowser.Common.Api; + +using Jellyfin.Plugin.SmartPlaylist.Lisp; + +namespace Jellyfin.Plugin.SmartPlaylist.Api { + [Serializable] + public class ProgramOutputDto { + public string Output { get; set; } + public string FinalExpression { get; set; } + public string Traceback { get; set; } + } + + [ApiController] + [Authorize(Policy = Policies.RequiresElevation)] + [Route("LispPlayground")] + [Produces(MediaTypeNames.Application.Json)] + public class LispPlaygroundController : ControllerBase { + private readonly ILogger _logger; + + public LispPlaygroundController( + ILogger logger + ) { + _logger = logger; + } + + private Executor SetupExecutor(StringBuilder sb) { + 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; + }; + executor.builtins["print"] = (x) => { + sb.Append(string.Join(" ", x.Select((i) => { + if (i is Lisp.String i_s) { + return i_s.Value(); + } + return i.ToString(); + }))); + return Lisp.Boolean.TRUE; + }; + executor.builtins["print"] = (x) => { + sb.Append(string.Join(" ", x.Select((i) => { + if (i is Lisp.String i_s) { + return i_s.Value(); + } + return i.ToString(); + }))); + sb.Append("\n"); + 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; + } + + [HttpPost] + [ProducesResponseType(StatusCodes.Status200OK)] + public async Task> SetPlaylist() { + try { + string program; + using (StreamReader reader = new StreamReader(Request.Body)) { + program = await reader.ReadToEndAsync(); + } + StringBuilder output = new StringBuilder(); + var e = SetupExecutor(output); + var r = e.eval(program).ToString(); + return Ok(new ProgramOutputDto() { + FinalExpression = r, + Output = output.ToString(), + }); + } catch (Exception ex) { + return Ok(new ProgramOutputDto() { + Traceback = ex.ToString(), + }); + } + } + } +} diff --git a/Jellyfin.Plugin.SmartPlaylist/Pages/lispPlayground.html b/Jellyfin.Plugin.SmartPlaylist/Pages/lispPlayground.html new file mode 100644 index 0000000..839b364 --- /dev/null +++ b/Jellyfin.Plugin.SmartPlaylist/Pages/lispPlayground.html @@ -0,0 +1,42 @@ + + + + + LispPlayground + + +
+
+
+
+
+ +
A program which should return t or nil to include or exclude the provided item.
+ +
+
+ +
The output of the program.
+ +
+
+ +
The final expression the program has been reduced to.
+ +
+
+ +
+
+
+
+ +
+ + diff --git a/Jellyfin.Plugin.SmartPlaylist/Pages/lispPlayground.js b/Jellyfin.Plugin.SmartPlaylist/Pages/lispPlayground.js new file mode 100644 index 0000000..65b2c93 --- /dev/null +++ b/Jellyfin.Plugin.SmartPlaylist/Pages/lispPlayground.js @@ -0,0 +1,43 @@ + +function fillForm(o) { + const output = document.querySelector('#LispPlaygroundOutput'); + const return_ = document.querySelector('#LispPlaygroundReturn'); + output.value = (o.hasOwnProperty("Output")) ? o.Output : o.Traceback; + return_.value = (o.hasOwnProperty("FinalExpression")) ? o.FinalExpression : ''; +} + +ApiClient.runLispProgram = function (program) { + const url = ApiClient.getUrl('LispPlayground'); + return this.ajax({ + type: 'POST', + url: url, + dataType: 'json', + contentType: 'text/plain; charset=UTF-8', + data: program, + }); +} +function initial_load() { + Dashboard.showLoadingMsg(); + Dashboard.hideLoadingMsg(); +} + +document.querySelector('#LispPlaygroundConfigPage') + .addEventListener('viewshow', function() { + initial_load(); + }); + +document.querySelector('#LispPlaygroundConfigPage') + .addEventListener('pageshow', function() { + initial_load(); + }); + +document.querySelector('#LispPlaygroundConfigForm') + .addEventListener('submit', function (e) { + e.preventDefault(); + Dashboard.showLoadingMsg(); + const editProgram = document.querySelector('#LispPlaygroundEditProgram'); + ApiClient.runLispProgram(editProgram.value).then(function (r) { + fillForm(r); + Dashboard.hideLoadingMsg(); + }); + }); diff --git a/Jellyfin.Plugin.SmartPlaylist/Plugin.cs b/Jellyfin.Plugin.SmartPlaylist/Plugin.cs index 0d962f6..04349c9 100644 --- a/Jellyfin.Plugin.SmartPlaylist/Plugin.cs +++ b/Jellyfin.Plugin.SmartPlaylist/Plugin.cs @@ -53,6 +53,16 @@ namespace Jellyfin.Plugin.SmartPlaylist { Name = "smartCollections.js", EmbeddedResourcePath = string.Format(CultureInfo.InvariantCulture, "{0}.Pages.smartCollections.js", GetType().Namespace), }, + new PluginPageInfo { + Name = "Lisp Playground", + DisplayName = "Lisp Playground", + EmbeddedResourcePath = string.Format(CultureInfo.InvariantCulture, "{0}.Pages.lispPlayground.html", GetType().Namespace), + EnableInMainMenu = true, + }, + new PluginPageInfo { + Name = "lispPlayground.js", + EmbeddedResourcePath = string.Format(CultureInfo.InvariantCulture, "{0}.Pages.lispPlayground.js", GetType().Namespace), + }, }; } } diff --git a/Jellyfin.Plugin.SmartPlaylist/jellyfin-smart-playlist.csproj b/Jellyfin.Plugin.SmartPlaylist/jellyfin-smart-playlist.csproj index 732ae1a..62c9b31 100644 --- a/Jellyfin.Plugin.SmartPlaylist/jellyfin-smart-playlist.csproj +++ b/Jellyfin.Plugin.SmartPlaylist/jellyfin-smart-playlist.csproj @@ -21,12 +21,16 @@ + + + +