Compare commits
No commits in common. "58353514012fbb89433588204352271ce87cc764" and "b5bab52c6ba2499e827ad6cf3017c7824ba9a0c1" have entirely different histories.
5835351401
...
b5bab52c6b
9 changed files with 47 additions and 54 deletions
|
@ -211,10 +211,7 @@ namespace Jellyfin.Plugin.SmartPlaylist.Lisp.Compiler {
|
||||||
public override string ToString() {
|
public override string ToString() {
|
||||||
return _value.ToString();
|
return _value.ToString();
|
||||||
}
|
}
|
||||||
public static Expression FromBase(object? o) {
|
public static Expression FromBase(object o) {
|
||||||
if (o == null) {
|
|
||||||
return new Boolean(false);
|
|
||||||
}
|
|
||||||
switch (o) {
|
switch (o) {
|
||||||
case bool b:
|
case bool b:
|
||||||
return new Boolean(b);
|
return new Boolean(b);
|
||||||
|
|
|
@ -24,13 +24,11 @@ namespace Jellyfin.Plugin.SmartPlaylist.Lisp.Compiler {
|
||||||
class SpaceToken : Token<string> {
|
class SpaceToken : Token<string> {
|
||||||
private SpaceToken(string value) : base(value) {}
|
private SpaceToken(string value) : base(value) {}
|
||||||
private static IToken<string>? take(CharStream program) {
|
private static IToken<string>? take(CharStream program) {
|
||||||
char[] spaces = [' ', '\n'];
|
|
||||||
if (program.Available() == 0) {
|
if (program.Available() == 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
var t = program.Get();
|
if (program.Get() == ' ') {
|
||||||
if (spaces.Contains(t)) {
|
return new SpaceToken(" ");
|
||||||
return new SpaceToken(t.ToString());
|
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -125,7 +125,6 @@ namespace Jellyfin.Plugin.SmartPlaylist.Lisp {
|
||||||
this["car"] = _car;
|
this["car"] = _car;
|
||||||
this["cdr"] = _cdr;
|
this["cdr"] = _cdr;
|
||||||
this["cons"] = _cons;
|
this["cons"] = _cons;
|
||||||
this["list"] = _list;
|
|
||||||
this["not"] = _not;
|
this["not"] = _not;
|
||||||
this["length"] = _length;
|
this["length"] = _length;
|
||||||
this["haskeys"] = _haskeys;
|
this["haskeys"] = _haskeys;
|
||||||
|
@ -261,9 +260,6 @@ namespace Jellyfin.Plugin.SmartPlaylist.Lisp {
|
||||||
}
|
}
|
||||||
throw new ApplicationException();
|
throw new ApplicationException();
|
||||||
}
|
}
|
||||||
private static Expression _list(IList<Expression> args) {
|
|
||||||
return new Compiler.List(args);
|
|
||||||
}
|
|
||||||
private static Expression _not(IList<Expression> args) {
|
private static Expression _not(IList<Expression> args) {
|
||||||
if (args[0] == new Compiler.Boolean(false)) {
|
if (args[0] == new Compiler.Boolean(false)) {
|
||||||
return new Compiler.Boolean(true);
|
return new Compiler.Boolean(true);
|
||||||
|
@ -341,7 +337,7 @@ namespace Jellyfin.Plugin.SmartPlaylist.Lisp {
|
||||||
}
|
}
|
||||||
private static Expression _define(Executor e, IList<Expression> args) {
|
private static Expression _define(Executor e, IList<Expression> args) {
|
||||||
var refname = ((Symbol) args[0]).name;
|
var refname = ((Symbol) args[0]).name;
|
||||||
e.environment.Set(refname, args[1]);
|
e.environment.Set(refname, e.eval(args[1]));
|
||||||
return new Compiler.Boolean(false); // NOOP
|
return new Compiler.Boolean(false); // NOOP
|
||||||
}
|
}
|
||||||
private static Expression _lambda(Executor e, IList<Expression> args) {
|
private static Expression _lambda(Executor e, IList<Expression> args) {
|
||||||
|
@ -401,9 +397,9 @@ namespace Jellyfin.Plugin.SmartPlaylist.Lisp {
|
||||||
public BuiltinsLater builtinsLater { get => _builtinsLater; }
|
public BuiltinsLater builtinsLater { get => _builtinsLater; }
|
||||||
|
|
||||||
public Expression EvalFunction(Symbol fcname, IList<Expression> args) {
|
public Expression EvalFunction(Symbol fcname, IList<Expression> args) {
|
||||||
if (_environment.Find(fcname.name) is IEnvironment<string, Expression> _e && _e != null) {
|
if (_environment.Find(fcname.name) is IEnvironment<string, Expression> _e) {
|
||||||
Expression first = _e.Get(fcname.name);
|
Expression? first = _e.Get(fcname.name);
|
||||||
return new List((new []{first}).Concat(args.ToArray()).Select(x => eval(x)).ToList());
|
return new List(new []{first}.ToList()) + new List(args.Select(x => eval(x)).ToList());
|
||||||
}
|
}
|
||||||
if (_builtins.ContainsKey(fcname.name)) {
|
if (_builtins.ContainsKey(fcname.name)) {
|
||||||
Function fc = _builtins[fcname.name];
|
Function fc = _builtins[fcname.name];
|
||||||
|
@ -420,10 +416,7 @@ namespace Jellyfin.Plugin.SmartPlaylist.Lisp {
|
||||||
public Expression eval(Expression expression) {
|
public Expression eval(Expression expression) {
|
||||||
switch (expression) {
|
switch (expression) {
|
||||||
case Symbol s:
|
case Symbol s:
|
||||||
if (_environment.Find(s.name) is not IEnvironment<string, Expression> env) {
|
return _environment.Find(s.name).Get(s.name);
|
||||||
throw new ApplicationException($"Could not find '{s.name}'");
|
|
||||||
}
|
|
||||||
return env.Get(s.name);
|
|
||||||
case Compiler.Boolean b:
|
case Compiler.Boolean b:
|
||||||
return b;
|
return b;
|
||||||
case Integer i:
|
case Integer i:
|
||||||
|
@ -438,14 +431,16 @@ namespace Jellyfin.Plugin.SmartPlaylist.Lisp {
|
||||||
if (list.expressions.Count == 0) {
|
if (list.expressions.Count == 0) {
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
if (list.expressions[0] is Symbol fc_symbol) {
|
// do we really want to allow shadowing of builtins?
|
||||||
return eval(EvalFunction(fc_symbol, list.expressions.Skip(1).ToList()));
|
if (list.expressions[0].GetType() == typeof(Symbol)) {
|
||||||
|
return eval(EvalFunction((Symbol) list.expressions[0], list.expressions.Skip(1).ToList()));
|
||||||
}
|
}
|
||||||
if (list.expressions[0] is Procedure procedure) {
|
if (list.expressions[0].GetType() == typeof(Procedure)) {
|
||||||
|
Procedure procedure = (Procedure) list.expressions[0];
|
||||||
return eval(procedure.Call(this, list.expressions.Skip(1).ToList()));
|
return eval(procedure.Call(this, list.expressions.Skip(1).ToList()));
|
||||||
}
|
}
|
||||||
var l = new Compiler.List(list.expressions.Select(x => eval(x)).ToList());
|
var l = new List(list.expressions.Select(x => eval(x)).ToList());
|
||||||
if (l.expressions[0] is Symbol|| l.expressions[0] is Procedure) {
|
if (l.expressions[0].GetType() == typeof(Procedure)) {
|
||||||
return eval(l);
|
return eval(l);
|
||||||
}
|
}
|
||||||
return l;
|
return l;
|
||||||
|
|
|
@ -1,4 +1,9 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using MediaBrowser.Common.Configuration;
|
||||||
|
using MediaBrowser.Common.Plugins;
|
||||||
using MediaBrowser.Model.Plugins;
|
using MediaBrowser.Model.Plugins;
|
||||||
|
using MediaBrowser.Model.Serialization;
|
||||||
|
|
||||||
namespace Jellyfin.Plugin.SmartPlaylist {
|
namespace Jellyfin.Plugin.SmartPlaylist {
|
||||||
public class PluginConfiguration : BasePluginConfiguration {
|
public class PluginConfiguration : BasePluginConfiguration {
|
||||||
|
|
|
@ -5,11 +5,23 @@ using MediaBrowser.Controller.Library;
|
||||||
using MediaBrowser.Controller.Playlists;
|
using MediaBrowser.Controller.Playlists;
|
||||||
using MediaBrowser.Controller.Providers;
|
using MediaBrowser.Controller.Providers;
|
||||||
using MediaBrowser.Model.IO;
|
using MediaBrowser.Model.IO;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using Jellyfin.Data.Entities;
|
using Jellyfin.Data.Entities;
|
||||||
using Jellyfin.Data.Enums;
|
using Jellyfin.Data.Enums;
|
||||||
using MediaBrowser.Model.Entities;
|
using MediaBrowser.Model.Entities;
|
||||||
|
using MediaBrowser.Controller;
|
||||||
using MediaBrowser.Controller.Entities;
|
using MediaBrowser.Controller.Entities;
|
||||||
|
using MediaBrowser.Controller.Library;
|
||||||
|
using MediaBrowser.Controller.Playlists;
|
||||||
|
using MediaBrowser.Controller.Providers;
|
||||||
|
using MediaBrowser.Model.IO;
|
||||||
using MediaBrowser.Model.Playlists;
|
using MediaBrowser.Model.Playlists;
|
||||||
|
using MediaBrowser.Model.Tasks;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
using Jellyfin.Plugin.SmartPlaylist.Lisp;
|
using Jellyfin.Plugin.SmartPlaylist.Lisp;
|
||||||
using Jellyfin.Plugin.SmartPlaylist.Lisp.Compiler;
|
using Jellyfin.Plugin.SmartPlaylist.Lisp.Compiler;
|
||||||
|
@ -81,7 +93,7 @@ namespace Jellyfin.Plugin.SmartPlaylist.ScheduledTasks {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private PlaylistId CreateNewPlaylist(string name, UserId userId) {
|
private SmartPlaylistId CreateNewPlaylist(string name, UserId userId) {
|
||||||
_logger.LogDebug("Creating playlist '{0}'", name);
|
_logger.LogDebug("Creating playlist '{0}'", name);
|
||||||
var req = new PlaylistCreationRequest {
|
var req = new PlaylistCreationRequest {
|
||||||
Name = name,
|
Name = name,
|
||||||
|
|
|
@ -50,7 +50,7 @@ namespace Jellyfin.Plugin.SmartPlaylist {
|
||||||
public bool Enabled { get; set; }
|
public bool Enabled { get; set; }
|
||||||
|
|
||||||
public SmartPlaylistDto() {
|
public SmartPlaylistDto() {
|
||||||
Id = "";
|
Id = Guid.NewGuid();
|
||||||
Playlists = [];
|
Playlists = [];
|
||||||
Name = Id.ToString();
|
Name = Id.ToString();
|
||||||
Program = DEFAULT_PROGRAM;
|
Program = DEFAULT_PROGRAM;
|
||||||
|
@ -62,7 +62,7 @@ namespace Jellyfin.Plugin.SmartPlaylist {
|
||||||
if (info.GetValue("Id", typeof(SmartPlaylistId)) is SmartPlaylistId _Id) {
|
if (info.GetValue("Id", typeof(SmartPlaylistId)) is SmartPlaylistId _Id) {
|
||||||
Id = _Id;
|
Id = _Id;
|
||||||
} else {
|
} else {
|
||||||
Id = "";
|
Id = Guid.NewGuid();
|
||||||
}
|
}
|
||||||
if (info.GetValue("Playlists", typeof(SmartPlaylistLinkDto[])) is SmartPlaylistLinkDto[] _Playlists) {
|
if (info.GetValue("Playlists", typeof(SmartPlaylistLinkDto[])) is SmartPlaylistLinkDto[] _Playlists) {
|
||||||
Playlists = _Playlists;
|
Playlists = _Playlists;
|
||||||
|
|
|
@ -19,12 +19,7 @@ namespace Jellyfin.Plugin.SmartPlaylist {
|
||||||
if (dto == null) {
|
if (dto == null) {
|
||||||
throw new ApplicationException("");
|
throw new ApplicationException("");
|
||||||
}
|
}
|
||||||
if (dto.Id == Path.GetFileNameWithoutExtension(filename)) {
|
dto.Filename = filename;
|
||||||
dto.Id = Path.GetFileNameWithoutExtension(filename);
|
|
||||||
}
|
|
||||||
if (dto.Filename != filename) {
|
|
||||||
dto.Filename = filename;
|
|
||||||
}
|
|
||||||
return dto;
|
return dto;
|
||||||
}
|
}
|
||||||
public async Task<SmartPlaylistDto> GetSmartPlaylistAsync(SmartPlaylistId smartPlaylistId) {
|
public async Task<SmartPlaylistDto> GetSmartPlaylistAsync(SmartPlaylistId smartPlaylistId) {
|
||||||
|
|
|
@ -2,4 +2,4 @@ global using System;
|
||||||
|
|
||||||
global using UserId = System.Guid;
|
global using UserId = System.Guid;
|
||||||
global using PlaylistId = System.Guid;
|
global using PlaylistId = System.Guid;
|
||||||
global using SmartPlaylistId = string;
|
global using SmartPlaylistId = System.Guid;
|
||||||
|
|
|
@ -93,19 +93,19 @@ namespace Tests
|
||||||
e = new Executor().eval("(apply + (1 2))");
|
e = new Executor().eval("(apply + (1 2))");
|
||||||
Assert.Equal(((Integer) e).value, 3);
|
Assert.Equal(((Integer) e).value, 3);
|
||||||
|
|
||||||
e = new Executor().eval("(car (list 10 20 30))");
|
e = new Executor().eval("(car (10 20 30))");
|
||||||
Assert.Equal(((Integer) e).value, 10);
|
Assert.Equal(((Integer) e).value, 10);
|
||||||
|
|
||||||
e = new Executor().eval("(cdr (list 10 20 30))");
|
e = new Executor().eval("(cdr (10 20 30))");
|
||||||
Assert.Equal(string.Format("{0}", e), "(20 30)");
|
Assert.Equal(string.Format("{0}", e), "(20 30)");
|
||||||
|
|
||||||
e = new Executor().eval("(cons 1 3)");
|
e = new Executor().eval("(cons 1 3)");
|
||||||
Assert.Equal(string.Format("{0}", e), "(1 3)");
|
Assert.Equal(string.Format("{0}", e), "(1 3)");
|
||||||
|
|
||||||
e = new Executor().eval("(cons 1 (list 2 3))");
|
e = new Executor().eval("(cons 1 (2 3))");
|
||||||
Assert.Equal(string.Format("{0}", e), "(1 2 3)");
|
Assert.Equal(string.Format("{0}", e), "(1 2 3)");
|
||||||
|
|
||||||
e = new Executor().eval("(length (cons 1 (list 2 3)))");
|
e = new Executor().eval("(length (cons 1 (2 3)))");
|
||||||
Assert.Equal(string.Format("{0}", e), "3");
|
Assert.Equal(string.Format("{0}", e), "3");
|
||||||
|
|
||||||
e = new Executor().eval("(>= 2 2)");
|
e = new Executor().eval("(>= 2 2)");
|
||||||
|
@ -126,10 +126,10 @@ namespace Tests
|
||||||
e = new Executor().eval("(or nil 4)");
|
e = new Executor().eval("(or nil 4)");
|
||||||
Assert.Equal("4", e.ToString());
|
Assert.Equal("4", e.ToString());
|
||||||
|
|
||||||
e = new Executor().eval("(= (list 1 2) (list 1 2))");
|
e = new Executor().eval("(= (1 2) (1 2))");
|
||||||
Assert.Equal(e.ToString(), "t");
|
Assert.Equal(e.ToString(), "t");
|
||||||
|
|
||||||
e = new Executor().eval("(= (list 1 2 3) (list 1 2))");
|
e = new Executor().eval("(= (1 2 3) (1 2))");
|
||||||
Assert.Equal(e.ToString(), "nil");
|
Assert.Equal(e.ToString(), "nil");
|
||||||
}
|
}
|
||||||
[Fact]
|
[Fact]
|
||||||
|
@ -165,28 +165,19 @@ namespace Tests
|
||||||
r = e.eval("(begin (define fact (lambda (n) (if (<= n 1) 1 (* n (fact (- n 1)))))) (fact 10))");
|
r = e.eval("(begin (define fact (lambda (n) (if (<= n 1) 1 (* n (fact (- n 1)))))) (fact 10))");
|
||||||
Assert.Equal(string.Format("{0}", r), "3628800");
|
Assert.Equal(string.Format("{0}", r), "3628800");
|
||||||
|
|
||||||
r = e.eval("(begin (define find (lambda (item list) (if (= list ()) nil (if (= item (car list)) (car list) (find item (cdr list)))))) (find 3 (list 1 2 3 4)))");
|
r = e.eval("(begin (define find (lambda (item list) (if (= list ()) nil (if (= item (car list)) (car list) (find item (cdr list)))))) (find 3 (1 2 3 4)))");
|
||||||
Assert.Equal(string.Format("{0}", r), "3");
|
Assert.Equal(string.Format("{0}", r), "3");
|
||||||
|
|
||||||
e = new Executor();
|
e = new Executor();
|
||||||
r = e.eval("(begin (define find (lambda (item list) (if (= list ()) nil (if (= item (car list)) (car list) (find item (cdr list)))))) (find 0 (list 1 2 3 4)))");
|
r = e.eval("(begin (define find (lambda (item list) (if (= list ()) nil (if (= item (car list)) (car list) (find item (cdr list)))))) (find 0 (1 2 3 4)))");
|
||||||
Assert.Equal(string.Format("{0}", r), "nil");
|
Assert.Equal(string.Format("{0}", r), "nil");
|
||||||
|
|
||||||
e = new Executor();
|
|
||||||
r = e.eval(@"
|
|
||||||
(begin
|
|
||||||
(define map (lambda (fc l) (if (= l ()) () (cons (fc (car l)) (map fc (cdr l))))))
|
|
||||||
(define multwo (lambda (x) (* 2 x)))
|
|
||||||
(map multwo (list 1 2 3)))
|
|
||||||
");
|
|
||||||
Assert.Equal(string.Format("{0}", r), "(2 4 6)");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public static void DefaultEnvironmentTest() {
|
public static void DefaultEnvironmentTest() {
|
||||||
Executor e = new Executor(new DefaultEnvironment());
|
Executor e = new Executor(new DefaultEnvironment());
|
||||||
Assert.Equal("nil", e.eval("(find 0 (list 1 2 3 4))").ToString());
|
Assert.Equal("nil", e.eval("(find 0 (1 2 3 4))").ToString());
|
||||||
Assert.Equal("3", e.eval("(find 3 (list 1 2 3 4))").ToString());
|
Assert.Equal("3", e.eval("(find 3 (1 2 3 4))").ToString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue