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(), }); } } } }