Compare commits

...

2 commits

3 changed files with 55 additions and 5 deletions

View file

@ -157,11 +157,35 @@ namespace Jellyfin.Plugin.SmartPlaylist.Lisp {
return new List<Expression>(); return new List<Expression>();
} }
} }
public class String: Scalar<string> { public class String: Scalar<string>, ISortable<String, Boolean> {
public String(string value) : base(value) {} public String(string value) : base(value) {}
public override string? ToString() { public override string? ToString() {
return $"\"{base.ToString()}\""; return $"\"{base.ToString()}\"";
} }
public static Boolean operator <(String a, String b) {
return (a.Value().CompareTo(b.Value()) < 0) ? Boolean.TRUE : Boolean.FALSE;
}
public static Boolean operator >(String a, String b) {
return b < a;
}
public static Boolean operator <=(String a, String b) {
return (a.Value().CompareTo(b.Value()) <= 0) ? Boolean.TRUE : Boolean.FALSE;
}
public static Boolean operator >=(String a, String b) {
return b <= a;
}
public override int GetHashCode() {
return base.GetHashCode();
}
public override bool Equals(object? other) {
return base.Equals(other);
}
public static Boolean operator ==(String a, String b) {
return (a._value == b._value) ? Boolean.TRUE : Boolean.FALSE;
}
public static Boolean operator !=(String a, String b) {
return (a._value != b._value) ? Boolean.TRUE : Boolean.FALSE;
}
} }
public class Cons: Expression { public class Cons: Expression {
public Expression Item1; public Expression Item1;

View file

@ -127,16 +127,21 @@ namespace Jellyfin.Plugin.SmartPlaylist.Lisp {
this["/"] = (x) => _agg((Integer a, Integer b) => a / b, x); this["/"] = (x) => _agg((Integer a, Integer b) => a / b, x);
this["%"] = (x) => _agg((Integer a, Integer b) => a % b, x); this["%"] = (x) => _agg((Integer a, Integer b) => a % b, x);
this["="] = (x) => _cmp((Integer a, Integer b) => a == b, x); this["="] = (x) => _cmp((Atom a, Atom b) => (a == b)? Boolean.TRUE : Boolean.FALSE, x);
this["eq?"] = (x) => _cmp((Integer a, Integer b) => a == b, x); this["!="] = (x) => _cmp((Atom a, Atom b) => (a != b)? Boolean.TRUE : Boolean.FALSE, x);
this["<"] = (x) => _cmp((Integer a, Integer b) => a < b, x); this["<"] = (x) => _cmp((Integer a, Integer b) => a < b, x);
this["<="] = (x) => _cmp((Integer a, Integer b) => a <= b, x); this["<="] = (x) => _cmp((Integer a, Integer b) => a <= b, x);
this[">"] = (x) => _cmp((Integer a, Integer b) => a > b, x); this[">"] = (x) => _cmp((Integer a, Integer b) => a > b, x);
this[">="] = (x) => _cmp((Integer a, Integer b) => a >= b, x); this[">="] = (x) => _cmp((Integer a, Integer b) => a >= b, x);
this["!="] = (x) => _cmp((Integer a, Integer b) => a != b, x);
this["not"] = (x) => { this["not"] = (x) => {
return (x.First() == Boolean.FALSE) ? Boolean.TRUE : Boolean.FALSE; return (x.First() == Boolean.FALSE) ? Boolean.TRUE : Boolean.FALSE;
}; };
this["string="] = (x) => _cmp((String a, String b) => a == b, x);
this["string!="] = (x) => _cmp((String a, String b) => a != b, x);
this["string>"] = (x) => _cmp((String a, String b) => a > b, x);
this["string>="] = (x) => _cmp((String a, String b) => a >= b, x);
this["string<"] = (x) => _cmp((String a, String b) => a < b, x);
this["string<="] = (x) => _cmp((String a, String b) => a <= b, x);
this["haskeys"] = _haskeys; this["haskeys"] = _haskeys;

View file

@ -29,6 +29,7 @@ Playlists:
UserId: 6eec632a-ff0d-4d09-aad0-bf9e90b14bc6 UserId: 6eec632a-ff0d-4d09-aad0-bf9e90b14bc6
Name: Rock Name: Rock
Program: (begin (invoke item "IsFavoriteOrLiked" (user))) Program: (begin (invoke item "IsFavoriteOrLiked" (user)))
SortProgram: (begin items)
Filename: /config/data/smartplaylists/Rock.yaml Filename: /config/data/smartplaylists/Rock.yaml
Enabled: true Enabled: true
``` ```
@ -84,7 +85,10 @@ still work and remember the correct playlist.
A lisp program to decide on a per item basis if it should be included in A lisp program to decide on a per item basis if it should be included in
the playlist, return `nil` to not include items, return any other value the playlist, return `nil` to not include items, return any other value
to include them. to include them. Global variables `user` and `item` are predefined
and contain a [User](https://github.com/jellyfin/jellyfin/blob/master/Jellyfin.Data/Entities/User.cs) and
[BaseItem](https://github.com/jellyfin/jellyfin/blob/master/MediaBrowser.Controller/Entities/BaseItem.cs)
respectively.
**!!! The filter expression will include all items matching, if you do **!!! The filter expression will include all items matching, if you do
not specify the kind of item to include/exclude all of them will be not specify the kind of item to include/exclude all of them will be
@ -98,6 +102,23 @@ The configuration page defines some useful functions to make it easier
to create filters. The above filter for liked items could be simplified to create filters. The above filter for liked items could be simplified
to: `(is-favourite)`. to: `(is-favourite)`.
### SortProgram
This works exactly like [Program](#program), but the input is the
user and a list of items (`items`) matched by [Program](#program).
The default is `(begin items)`, which doesn't sort at all. To sort
the items by name you could use the following program:
```lisp
(qsort
(lambda
(a b)
(string>
(car (getitems a "Name"))
(car (getitems b "Name"))))
items)
```
#### Available definitions #### Available definitions
- **lower**: lowercases a string (`(eq (lower "SomeString") "somestring")`) - **lower**: lowercases a string (`(eq (lower "SomeString") "somestring")`)