# Jellyfin SmartPlaylist Plugin

Smart playlists with Lisp filter engine.

This readme contains instructions for the most recent changes in
the development branch (`main`). To view the file appropriate
for your version select the tag corresponding to your version.
The latest version is [v0.3.0.0](https://gitea.redxef.at/redxef/jellyfin-smart-playlist/src/tag/v0.3.0.0).

## How to use

After [installing](#installation) the plugin and restarting Jellyfin
create a empty file in `config/data/smartplaylists` like this, maybe
you want to generate a playlist of your favourite rock songs:

```
$ touch config/data/smartplaylists/Rock.yaml
```

Afterwards run the Task `(re)generate Smart Playlists`, this will rename
the `yaml` file and populate it with some default values. You can now
adjust the file to your liking. [Go here](examples.md) to see more
examples.

```yaml
Id: Rock
Playlists:
- PlaylistId: 24f12e1e-3278-d6d6-0ca4-066e93296c95
  UserId: 6eec632a-ff0d-4d09-aad0-bf9e90b14bc6
Name: Rock
Program: (begin (invoke item "IsFavoriteOrLiked" (list *user*)))
SortProgram: (begin items)
Filename: /config/data/smartplaylists/Rock.yaml
Enabled: true
```

This is the default configuration and will always match all your
favorite songs (and songs which are in favourited albums).

To change the filter you can append a `|` (pipe) to the Program
line and write multiline filters like this:

```yaml
Porgram: |
  (begin
    (invoke item "IsFavoriteOrLiked" (list *user*)))
```

This is equivalent to the above example (not counting the other
fields obviously).


### Id

Arbitrary Id assigned to this playlist, can usually be left alone.

### Playlists

A list of Playlist/User mappings. By default all users get an entry.

The ids must have the dashes in them as of now. To convert a id
from without dashes to the canonical form run this command:

`echo '<your id here>' | python3 -c 'import uuid; import sys; print(uuid.UUID(sys.stdin.read().strip()))'`

To get your user id navigate to your user profile and copy the part
after `userId` in the address bar.

#### PlaylistId

The id of the playlist that should be managed, must be owned by the
corresponding user.

#### UserId

The user associated with this playlist.

### Name

The name of the generated playlists, this is just a default value.
If the user changes the name of their playlist the plugin will
still work and remember the correct playlist.

### Program

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
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
not specify the kind of item to include/exclude all of them will be
added. Should you allow a playlist to be included all of it's items
will be added to the generated playlist !!!**

It's best to be explicit and always specify the item kinds you want to
include: `(and (or (is-type "MusicAlbum") (is-type "Audio")) . rest of filter)`.

The configuration page defines some useful functions to make it easier
to create filters. The above filter for liked items could be simplified
to: `(is-favourite)`.

*Go [here](lisp.md) to get an overview of the built-in functions.*

### 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*)
```

### Filename

The path to this file, only used internally and updated by the program.

### Enabled

Enable this playlist, currently ignored.


## Installation

Add the [plugin repository](https://jellyfin.org/docs/general/server/plugins/#catalog)
to Jellyfin:
`https://gitea.redxef.at/redxef/jellyfin-smart-playlist/raw/branch/manifest/manifest.json`

Go to `Dashboard>Catalog>(Gear)>(Plus)` and paste the provided link into
the field labeled `Repository URL`, give the plugin a descriptive name
too.

## Releasing a new version

1. Write the changelog: `git log --oneline $prev_version..`
2. Update the following files to include up-to-date version numbers
   and changelogs, if applicable:
   - `README.md`
   - `Jellyfin.Plugin.SmartPlaylist/build.yaml`
   - `Jellyfin.Plugin.SmartPlaylist/jellyfin-smart-playlist.csproj`
   Don't forget to also bump the ABI version of Jellyfin.
3. Push the changes
4. Create a new release with the changelog, mark as pre-release if
   applicable.
5. Done! The build pipeline will do the rest.