From a00b01c577e4c7e2188d91f395c53345ef729c5a Mon Sep 17 00:00:00 2001 From: redxef Date: Wed, 26 Feb 2025 23:36:52 +0100 Subject: [PATCH] feat: allow specifying items as inputs for the lisp playground. Refs: #6 --- .../Api/LispPlaygroundController.cs | 43 ++++++++++++++++--- .../Pages/lispPlayground.html | 15 +++++-- .../Pages/lispPlayground.js | 12 ++++-- 3 files changed, 56 insertions(+), 14 deletions(-) diff --git a/Jellyfin.Plugin.SmartPlaylist/Api/LispPlaygroundController.cs b/Jellyfin.Plugin.SmartPlaylist/Api/LispPlaygroundController.cs index 2b4278e..f5fe275 100644 --- a/Jellyfin.Plugin.SmartPlaylist/Api/LispPlaygroundController.cs +++ b/Jellyfin.Plugin.SmartPlaylist/Api/LispPlaygroundController.cs @@ -1,15 +1,26 @@ using System.Net.Mime; using System.Text; +using System.Web; 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 MediaBrowser.Controller.Library; + +using YamlDotNet.Serialization; using Jellyfin.Plugin.SmartPlaylist.Lisp; namespace Jellyfin.Plugin.SmartPlaylist.Api { + + [Serializable] + public class ProgramInputDto { + public string InputLinks { get; set; } + public string Program { get; set; } + } + [Serializable] public class ProgramOutputDto { public string Output { get; set; } @@ -23,16 +34,21 @@ namespace Jellyfin.Plugin.SmartPlaylist.Api { [Produces(MediaTypeNames.Application.Json)] public class LispPlaygroundController : ControllerBase { private readonly ILogger _logger; + private readonly ILibraryManager _libraryManager; public LispPlaygroundController( - ILogger logger + ILogger logger, + ILibraryManager libraryManager ) { _logger = logger; + _libraryManager = libraryManager; } - private Executor SetupExecutor(StringBuilder sb) { + private Executor SetupExecutor(StringBuilder sb, IList inputs) { var env = new DefaultEnvironment(); var executor = new Executor(env); + + env["*items*"] = Lisp.Cons.FromList(inputs.Select(x => _libraryManager.GetItemById(x)).Select(x => Lisp.Object.FromBase(x)).ToList()); executor.builtins["logd"] = (x) => { _logger.LogDebug(((Lisp.String)x.First()).Value(), x.Skip(1).ToArray()); return Lisp.Boolean.TRUE; @@ -76,17 +92,32 @@ namespace Jellyfin.Plugin.SmartPlaylist.Api { return executor; } + private List extractItemIds(string s) { + List r = new List(); + foreach (string line in s.Split("\n".ToCharArray(), StringSplitOptions.RemoveEmptyEntries)) { + var uri = new Uri(line); + uri = new Uri("http://some-domain.tld" + uri.Fragment.Substring(1)); + var id = HttpUtility.ParseQueryString(uri.Query).Get("id"); + if (id == null) { + continue; + } + r.Add(id); + } + return r; + } + [HttpPost] [ProducesResponseType(StatusCodes.Status200OK)] public async Task> SetPlaylist() { try { - string program; + string input; using (StreamReader reader = new StreamReader(Request.Body)) { - program = await reader.ReadToEndAsync(); + input = await reader.ReadToEndAsync(); } + var dto = new DeserializerBuilder().Build().Deserialize(input); StringBuilder output = new StringBuilder(); - var e = SetupExecutor(output); - var r = e.eval(program).ToString(); + var e = SetupExecutor(output, extractItemIds(dto.InputLinks)); + var r = e.eval(dto.Program).ToString(); return Ok(new ProgramOutputDto() { FinalExpression = r, Output = output.ToString(), diff --git a/Jellyfin.Plugin.SmartPlaylist/Pages/lispPlayground.html b/Jellyfin.Plugin.SmartPlaylist/Pages/lispPlayground.html index 839b364..3c7a587 100644 --- a/Jellyfin.Plugin.SmartPlaylist/Pages/lispPlayground.html +++ b/Jellyfin.Plugin.SmartPlaylist/Pages/lispPlayground.html @@ -9,20 +9,27 @@
+
+ +
+ Items which should be given to the filter program, one link per line. + The program will get this list in the variable *items*. + +
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 index 8b3d4f3..d70738b 100644 --- a/Jellyfin.Plugin.SmartPlaylist/Pages/lispPlayground.js +++ b/Jellyfin.Plugin.SmartPlaylist/Pages/lispPlayground.js @@ -6,14 +6,17 @@ function fillForm(o) { return_.value = (o.hasOwnProperty("FinalExpression")) ? o.FinalExpression : ''; } -ApiClient.runLispProgram = function (program) { +ApiClient.runLispProgram = function (inputLinks, program) { const url = ApiClient.getUrl('LispPlayground'); return this.ajax({ type: 'POST', url: url, dataType: 'json', - contentType: 'text/plain; charset=UTF-8', - data: program, + contentType: 'application/json; charset=UTF-8', + data: JSON.stringify({ + InputLinks: inputLinks, + Program: program, + }), }); } function initial_load() { @@ -37,8 +40,9 @@ export default function (view, params) { .addEventListener('submit', function (e) { e.preventDefault(); Dashboard.showLoadingMsg(); + const editInputLinks = document.querySelector('#LispPlaygroundInputLinks'); const editProgram = document.querySelector('#LispPlaygroundEditProgram'); - ApiClient.runLispProgram(editProgram.value).then(function (r) { + ApiClient.runLispProgram(editInputLinks.value, editProgram.value).then(function (r) { fillForm(r); Dashboard.hideLoadingMsg(); });