Cleanup and allow calling methods with arguments.
This commit is contained in:
parent
6208c9c070
commit
faca13d393
6 changed files with 103 additions and 42 deletions
|
@ -31,8 +31,8 @@ namespace Jellyfin.Plugin.SmartPlaylist.Lisp.Compiler {
|
|||
E Equals(T other);
|
||||
}
|
||||
|
||||
public abstract class Expression: IFormattable, IComparable<Expression, bool> {
|
||||
public abstract string ToString(string? format, IFormatProvider? provider);
|
||||
public abstract class Expression: IComparable<Expression, bool> {
|
||||
public override abstract string ToString();
|
||||
public abstract override int GetHashCode();
|
||||
public abstract bool Equals(Expression other);
|
||||
public override bool Equals(object? other) {
|
||||
|
@ -47,6 +47,7 @@ namespace Jellyfin.Plugin.SmartPlaylist.Lisp.Compiler {
|
|||
public static bool operator !=(Expression left, Expression right) {
|
||||
return !left.Equals(right);
|
||||
}
|
||||
public abstract object Inner();
|
||||
}
|
||||
public abstract class Atom : Expression {}
|
||||
public class Symbol : Atom {
|
||||
|
@ -67,7 +68,10 @@ namespace Jellyfin.Plugin.SmartPlaylist.Lisp.Compiler {
|
|||
}
|
||||
return false;
|
||||
}
|
||||
public override string ToString(string? format, IFormatProvider? provider) {
|
||||
public override string ToString() {
|
||||
return _name;
|
||||
}
|
||||
public override object Inner() {
|
||||
return _name;
|
||||
}
|
||||
}
|
||||
|
@ -90,9 +94,12 @@ namespace Jellyfin.Plugin.SmartPlaylist.Lisp.Compiler {
|
|||
}
|
||||
return false;
|
||||
}
|
||||
public override string ToString(string? format, IFormatProvider? provider) {
|
||||
public override string ToString() {
|
||||
return _value? "t" : "nil";
|
||||
}
|
||||
public override object Inner() {
|
||||
return _value;
|
||||
}
|
||||
}
|
||||
|
||||
public class Integer : Atom, IAddable<Integer>, ISubtractable<Integer>, IMultiplicatable<Integer>, IDivisible<Integer>, ISortable<Integer, Boolean> {
|
||||
|
@ -113,9 +120,8 @@ namespace Jellyfin.Plugin.SmartPlaylist.Lisp.Compiler {
|
|||
}
|
||||
return false;
|
||||
}
|
||||
public override string ToString(string? format, IFormatProvider? provider) {
|
||||
public override string ToString() {
|
||||
return _value.ToString();
|
||||
//return _value.ToString("0", provider);
|
||||
}
|
||||
public static Integer operator +(Integer a, Integer b) {
|
||||
return new Integer(a.value + b.value);
|
||||
|
@ -150,6 +156,9 @@ namespace Jellyfin.Plugin.SmartPlaylist.Lisp.Compiler {
|
|||
public static Boolean operator !=(Integer a, Integer b) {
|
||||
return new Boolean(a.value != b.value);
|
||||
}
|
||||
public override object Inner() {
|
||||
return _value;
|
||||
}
|
||||
}
|
||||
|
||||
public class String : Atom, IAddable<String> {
|
||||
|
@ -170,12 +179,15 @@ namespace Jellyfin.Plugin.SmartPlaylist.Lisp.Compiler {
|
|||
}
|
||||
return false;
|
||||
}
|
||||
public override string ToString(string? format, IFormatProvider? provider) {
|
||||
public override string ToString() {
|
||||
return "\"" + _value + "\"";
|
||||
}
|
||||
public static String operator +(String a, String b) {
|
||||
return new String (a.value + b.value);
|
||||
}
|
||||
public override object Inner() {
|
||||
return _value;
|
||||
}
|
||||
}
|
||||
|
||||
public class Object : Atom {
|
||||
|
@ -196,7 +208,7 @@ namespace Jellyfin.Plugin.SmartPlaylist.Lisp.Compiler {
|
|||
}
|
||||
return false;
|
||||
}
|
||||
public override string ToString(string? format, IFormatProvider? provider) {
|
||||
public override string ToString() {
|
||||
return _value.ToString();
|
||||
}
|
||||
public static Atom FromBase(object o) {
|
||||
|
@ -211,6 +223,9 @@ namespace Jellyfin.Plugin.SmartPlaylist.Lisp.Compiler {
|
|||
return new Object(o);
|
||||
}
|
||||
}
|
||||
public override object Inner() {
|
||||
return _value;
|
||||
}
|
||||
}
|
||||
|
||||
public class List : Expression {
|
||||
|
@ -233,8 +248,8 @@ namespace Jellyfin.Plugin.SmartPlaylist.Lisp.Compiler {
|
|||
}
|
||||
return false;
|
||||
}
|
||||
public override string ToString(string? format, IFormatProvider? provider) {
|
||||
return "(" + string.Join(" ", _expressions.Select(x => x.ToString("0", provider))) + ")";
|
||||
public override string ToString() {
|
||||
return "(" + string.Join(" ", _expressions.Select(x => x.ToString())) + ")";
|
||||
}
|
||||
public static List operator +(List a, List b) {
|
||||
List<Expression> r = new List<Expression>();
|
||||
|
@ -242,6 +257,9 @@ namespace Jellyfin.Plugin.SmartPlaylist.Lisp.Compiler {
|
|||
r.AddRange(b.expressions);
|
||||
return new List(r);
|
||||
}
|
||||
public override object Inner() {
|
||||
return _expressions.Select(x => x.Inner()).ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
public class Parser {
|
||||
|
|
|
@ -30,6 +30,7 @@ namespace Jellyfin.Plugin.SmartPlaylist.Lisp {
|
|||
this["length"] = _length;
|
||||
this["haskeys"] = _haskeys;
|
||||
this["getitems"] = _getitems;
|
||||
this["invoke"] = _invoke;
|
||||
//this[new Symbol("!=")] = _ne;
|
||||
}
|
||||
|
||||
|
@ -202,20 +203,27 @@ namespace Jellyfin.Plugin.SmartPlaylist.Lisp {
|
|||
r.Add(Compiler.Object.FromBase(pi.GetValue(o.value)));
|
||||
continue;
|
||||
}
|
||||
MethodInfo? mi = o.value.GetType().GetMethod(s.value);
|
||||
if (mi != null) {
|
||||
r.Add(Compiler.Object.FromBase(mi.Invoke(o.value, null)));
|
||||
continue;
|
||||
}
|
||||
FieldInfo? fi = o.value.GetType().GetField(s.value);
|
||||
if (fi != null) {
|
||||
r.Add(Compiler.Object.FromBase(fi.GetValue(o.value)));
|
||||
continue;
|
||||
}
|
||||
throw new ApplicationException();
|
||||
throw new ApplicationException($"{o.value} has no property or field {s.value}");
|
||||
}
|
||||
return new Compiler.List(r);
|
||||
}
|
||||
|
||||
private static Expression _invoke(IList<Expression> args) {
|
||||
Compiler.Object o = (Compiler.Object) args[0];
|
||||
Compiler.String s = (Compiler.String) args[1];
|
||||
Compiler.List l = (Compiler.List) args[2];
|
||||
IList<Expression> r = new List<Expression>();
|
||||
MethodInfo? mi = o.value.GetType().GetMethod(s.value);
|
||||
if (mi == null) {
|
||||
throw new ApplicationException($"{o.value} has not method {s.value}");
|
||||
}
|
||||
return Compiler.Object.FromBase(mi.Invoke(o.value, (object?[]?) l.Inner()));
|
||||
}
|
||||
}
|
||||
|
||||
public class BuiltinsLater : Dictionary<string, FunctionLater> {
|
||||
|
|
|
@ -38,6 +38,8 @@ namespace Jellyfin.Plugin.SmartPlaylist.ScheduledTasks {
|
|||
private readonly ILogger _logger;
|
||||
private readonly ILibraryManager _libraryManager;
|
||||
private readonly IUserManager _userManager;
|
||||
private readonly IProviderManager _providerManager;
|
||||
private readonly IFileSystem _fileSystem;
|
||||
private readonly IPlaylistManager _playlistManager;
|
||||
|
||||
private readonly IStore _store;
|
||||
|
@ -46,15 +48,19 @@ namespace Jellyfin.Plugin.SmartPlaylist.ScheduledTasks {
|
|||
ILogger<Plugin> logger,
|
||||
ILibraryManager libraryManager,
|
||||
IUserManager userManager,
|
||||
IProviderManager providerManager,
|
||||
IFileSystem fileSystem,
|
||||
IPlaylistManager playlistManager,
|
||||
IServerApplicationPaths serverApplicationPaths
|
||||
) {
|
||||
_logger = logger;
|
||||
_libraryManager = libraryManager;
|
||||
_userManager = userManager;
|
||||
_providerManager = providerManager;
|
||||
_fileSystem = fileSystem;
|
||||
_playlistManager = playlistManager;
|
||||
|
||||
_store = new Store(new FileSystem(serverApplicationPaths));
|
||||
_store = new Store(new SmartPlaylistFileSystem(serverApplicationPaths));
|
||||
}
|
||||
|
||||
public string Category => "Library";
|
||||
|
@ -84,24 +90,29 @@ namespace Jellyfin.Plugin.SmartPlaylist.ScheduledTasks {
|
|||
}
|
||||
}
|
||||
|
||||
private SmartPlaylistId CreateNewPlaylist(SmartPlaylistDto dto, User user) {
|
||||
private SmartPlaylistId CreateNewPlaylist(SmartPlaylistDto dto) {
|
||||
var req = new PlaylistCreationRequest {
|
||||
Name = dto.Name,
|
||||
UserId = user.Id
|
||||
UserId = dto.User
|
||||
};
|
||||
return Guid.Parse(_playlistManager.CreatePlaylist(req).Result.Id);
|
||||
var playlistGuid = Guid.Parse(_playlistManager.CreatePlaylist(req).Result.Id);
|
||||
return playlistGuid;
|
||||
}
|
||||
|
||||
private IEnumerable<Guid> FilterPlaylistItems(IEnumerable<BaseItem> items, User user, SmartPlaylistDto smartPlaylist) {
|
||||
List<Guid> results = new List<Guid>();
|
||||
Expression expression = new Parser(StringTokenStream.generate(smartPlaylist.Program)).parse();
|
||||
Executor executor = new Executor();
|
||||
executor.environment["user"] = new Lisp_Object(user);
|
||||
foreach (var i in items) {
|
||||
executor.environment["item"] = new Lisp_Object(i);
|
||||
var r = executor.eval(expression);
|
||||
_logger.LogInformation("Item {0} evaluated to {1}", i, r.ToString());
|
||||
_logger.LogDebug("Item {0} evaluated to {1}", i, r.ToString());
|
||||
if (r is Lisp_Boolean r_bool) {
|
||||
if (r_bool.value) { results.Add(i.Id); }
|
||||
if (r_bool.value) {
|
||||
_logger.LogDebug("Added "{0}" to Smart Playlist {1}", i, smartPlaylist.Name);
|
||||
results.Add(i.Id);
|
||||
}
|
||||
} else {
|
||||
_logger.LogInformation("Program did not return a boolean, returned {0}", r.ToString());
|
||||
}
|
||||
|
@ -124,8 +135,8 @@ namespace Jellyfin.Plugin.SmartPlaylist.ScheduledTasks {
|
|||
List<Playlist> playlists = _playlistManager.GetPlaylists(user.Id).Where(x => x.Id == dto.Id).ToList();
|
||||
if ((dto.Id == null) || !playlists.Any()) {
|
||||
_logger.LogInformation("Generating new smart playlist (dto.Id = {0}, playlists.Any() = {1})", dto.Id, playlists.Any());
|
||||
_store.DeleteSmartPlaylist(dto.Id);
|
||||
dto.Id = CreateNewPlaylist(dto, user);
|
||||
_store.DeleteSmartPlaylist(dto);
|
||||
dto.Id = CreateNewPlaylist(dto);
|
||||
await _store.SaveSmartPlaylistAsync(dto);
|
||||
playlists = _playlistManager.GetPlaylists(user.Id).Where(x => x.Id == dto.Id).ToList();
|
||||
}
|
||||
|
@ -137,13 +148,12 @@ namespace Jellyfin.Plugin.SmartPlaylist.ScheduledTasks {
|
|||
}
|
||||
|
||||
private async Task ClearPlaylist(SmartPlaylistDto smartPlaylist, Playlist playlist, User user) {
|
||||
var req = new InternalItemsQuery(user)
|
||||
{
|
||||
IncludeItemTypes = AvailableFilterItems,
|
||||
Recursive = true
|
||||
};
|
||||
var existingItems = playlist.GetChildren(user, false, req).Select(x => x.Id.ToString()).ToList();
|
||||
await _playlistManager.RemoveItemFromPlaylistAsync(playlist.Id.ToString(), existingItems);
|
||||
// fuck if I know
|
||||
if (_libraryManager.GetItemById(playlist.Id) is not Playlist playlist_new) {
|
||||
throw new ArgumentException("");
|
||||
}
|
||||
var existingItems = playlist_new.GetManageableItems().ToList();
|
||||
await _playlistManager.RemoveItemFromPlaylistAsync(playlist.Id.ToString(), existingItems.Select(x => x.Item1.Id));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,28 @@
|
|||
using System.Runtime.Serialization;
|
||||
namespace Jellyfin.Plugin.SmartPlaylist {
|
||||
[Serializable]
|
||||
public class SmartPlaylistDto {
|
||||
private string DEFAULT_PROGRAM = "(begin (invoke item 'IsFavoriteOrLiked' (user)))";
|
||||
public SmartPlaylistId Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string FileName { get; set; }
|
||||
public string? Name { get; set; }
|
||||
public UserId User { get; set; }
|
||||
public string Program { get; set; }
|
||||
public int MaxItems { get; set; }
|
||||
public string? Program { get; set; }
|
||||
public string? Filename { get; set; }
|
||||
public int MaxItems { get; set; } = -1;
|
||||
|
||||
public void Fill(string filename) {
|
||||
if (Id == Guid.Empty) {
|
||||
Id = Guid.NewGuid();
|
||||
}
|
||||
if (Name == null) {
|
||||
Name = Id.ToString();
|
||||
}
|
||||
if (Program == null) {
|
||||
Program = DEFAULT_PROGRAM;
|
||||
}
|
||||
if (Filename == null) {
|
||||
Filename = filename;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,8 +9,8 @@ namespace Jellyfin.Plugin.SmartPlaylist {
|
|||
public string[] FindAllSmartPlaylistFilePaths();
|
||||
}
|
||||
|
||||
public class FileSystem : ISmartPlaylistFileSystem {
|
||||
public FileSystem(IServerApplicationPaths serverApplicationPaths) {
|
||||
public class SmartPlaylistFileSystem : ISmartPlaylistFileSystem {
|
||||
public SmartPlaylistFileSystem(IServerApplicationPaths serverApplicationPaths) {
|
||||
StoragePath = Path.Combine(serverApplicationPaths.DataPath, "smartplaylists");
|
||||
if (!Directory.Exists(StoragePath)) { Directory.CreateDirectory(StoragePath); }
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ namespace Jellyfin.Plugin.SmartPlaylist {
|
|||
Task<SmartPlaylistDto?> GetSmartPlaylistAsync(SmartPlaylistId smartPlaylistId);
|
||||
Task<SmartPlaylistDto[]> GetAllSmartPlaylistsAsync();
|
||||
Task SaveSmartPlaylistAsync(SmartPlaylistDto smartPlaylist);
|
||||
void DeleteSmartPlaylist(SmartPlaylistId smartPlaylistId);
|
||||
void DeleteSmartPlaylist(SmartPlaylistDto smartPlaylist);
|
||||
}
|
||||
|
||||
public class Store : IStore {
|
||||
|
@ -15,7 +15,9 @@ namespace Jellyfin.Plugin.SmartPlaylist {
|
|||
}
|
||||
private async Task<SmartPlaylistDto?> LoadPlaylistAsync(string filename) {
|
||||
await using var r = File.OpenRead(filename);
|
||||
return await JsonSerializer.DeserializeAsync<SmartPlaylistDto>(r).ConfigureAwait(false);
|
||||
var dto = (await JsonSerializer.DeserializeAsync<SmartPlaylistDto>(r).ConfigureAwait(false));
|
||||
dto.Fill(filename);
|
||||
return dto;
|
||||
}
|
||||
public async Task<SmartPlaylistDto?> GetSmartPlaylistAsync(SmartPlaylistId smartPlaylistId) {
|
||||
string filename = _fileSystem.FindSmartPlaylistFilePath(smartPlaylistId);
|
||||
|
@ -31,9 +33,15 @@ namespace Jellyfin.Plugin.SmartPlaylist {
|
|||
await using var w = File.Create(filename);
|
||||
await JsonSerializer.SerializeAsync(w, smartPlaylist).ConfigureAwait(false);
|
||||
}
|
||||
public void DeleteSmartPlaylist(SmartPlaylistId smartPlaylistId) {
|
||||
string filename = _fileSystem.FindSmartPlaylistFilePath(smartPlaylistId);
|
||||
if (File.Exists(filename)) { File.Delete(filename); }
|
||||
private void DeleteSmartPlaylistById(SmartPlaylistId smartPlaylistId) {
|
||||
try {
|
||||
string filename = _fileSystem.FindSmartPlaylistFilePath(smartPlaylistId);
|
||||
if (File.Exists(filename)) { File.Delete(filename); }
|
||||
} catch (System.InvalidOperationException) {}
|
||||
}
|
||||
public void DeleteSmartPlaylist(SmartPlaylistDto smartPlaylist) {
|
||||
if (File.Exists(smartPlaylist.Filename)) { File.Delete(smartPlaylist.Filename); }
|
||||
DeleteSmartPlaylistById(smartPlaylist.Id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue