feat!: Allow one smartplaylist to generate multiple user playlists.
This commit is contained in:
parent
eb45ed5772
commit
6662c2e1e5
4 changed files with 140 additions and 38 deletions
|
@ -32,7 +32,9 @@ namespace Jellyfin.Plugin.SmartPlaylist.ScheduledTasks {
|
|||
public class GeneratePlaylist : IScheduledTask {
|
||||
|
||||
public static readonly BaseItemKind[] AvailableFilterItems = {
|
||||
BaseItemKind.Audio
|
||||
BaseItemKind.Audio,
|
||||
BaseItemKind.MusicAlbum,
|
||||
BaseItemKind.Playlist,
|
||||
};
|
||||
|
||||
private readonly ILogger _logger;
|
||||
|
@ -90,10 +92,10 @@ namespace Jellyfin.Plugin.SmartPlaylist.ScheduledTasks {
|
|||
}
|
||||
}
|
||||
|
||||
private SmartPlaylistId CreateNewPlaylist(SmartPlaylistDto dto) {
|
||||
private SmartPlaylistId CreateNewPlaylist(string name, UserId userId) {
|
||||
var req = new PlaylistCreationRequest {
|
||||
Name = dto.Name,
|
||||
UserId = dto.User
|
||||
Name = name,
|
||||
UserId = userId,
|
||||
};
|
||||
var playlistGuid = Guid.Parse(_playlistManager.CreatePlaylist(req).Result.Id);
|
||||
return playlistGuid;
|
||||
|
@ -107,7 +109,7 @@ namespace Jellyfin.Plugin.SmartPlaylist.ScheduledTasks {
|
|||
foreach (var i in items) {
|
||||
executor.environment["item"] = new Lisp_Object(i);
|
||||
var r = executor.eval(expression);
|
||||
_logger.LogDebug("Item {0} evaluated to {1}", i, r.ToString());
|
||||
_logger.LogTrace("Item {0} evaluated to {1}", i, r.ToString());
|
||||
if (r is Lisp_Boolean r_bool) {
|
||||
if (r_bool.value) {
|
||||
_logger.LogDebug("Added '{0}' to Smart Playlist {1}", i, smartPlaylist.Name);
|
||||
|
@ -122,7 +124,7 @@ namespace Jellyfin.Plugin.SmartPlaylist.ScheduledTasks {
|
|||
|
||||
private IEnumerable<BaseItem> GetAllUserMedia(User user) {
|
||||
var req = new InternalItemsQuery(user) {
|
||||
IncludeItemTypes = new[] {BaseItemKind.Audio},
|
||||
IncludeItemTypes = AvailableFilterItems,
|
||||
Recursive = true,
|
||||
};
|
||||
return _libraryManager.GetItemsResult(req).Items;
|
||||
|
@ -131,23 +133,44 @@ namespace Jellyfin.Plugin.SmartPlaylist.ScheduledTasks {
|
|||
public async Task ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken) {
|
||||
_logger.LogInformation("Started regenerate Smart Playlists");
|
||||
foreach (SmartPlaylistDto dto in await _store.GetAllSmartPlaylistsAsync()) {
|
||||
var user = _userManager.GetUserById(dto.User);
|
||||
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);
|
||||
dto.Id = CreateNewPlaylist(dto);
|
||||
await _store.SaveSmartPlaylistAsync(dto);
|
||||
playlists = _playlistManager.GetPlaylists(user.Id).Where(x => x.Id == dto.Id).ToList();
|
||||
var changedDto = false;
|
||||
if (dto.Playlists.Length == 0) {
|
||||
dto.Playlists = _userManager.UsersIds.Select(x => new SmartPlaylistLinkDto {
|
||||
UserId = x,
|
||||
PlaylistId = CreateNewPlaylist(dto.Name, x),
|
||||
}).ToArray();
|
||||
changedDto = true;
|
||||
}
|
||||
foreach (SmartPlaylistLinkDto playlistLink in dto.Playlists) {
|
||||
if (playlistLink.PlaylistId == Guid.Empty) {
|
||||
// not initialized
|
||||
playlistLink.PlaylistId = CreateNewPlaylist(dto.Name, playlistLink.UserId);
|
||||
changedDto = true;
|
||||
} else if (_playlistManager.GetPlaylists(playlistLink.UserId).Where(x => x.Id == playlistLink.PlaylistId).ToArray().Length == 0) {
|
||||
// somehow the corresponding playlist doesnt
|
||||
// exist anymore, did the user delete it?
|
||||
playlistLink.PlaylistId = CreateNewPlaylist(dto.Name, playlistLink.UserId);
|
||||
changedDto = true;
|
||||
}
|
||||
}
|
||||
if (changedDto) {
|
||||
_store.DeleteSmartPlaylist(dto); // delete in case the file was not the canonical one.
|
||||
await _store.SaveSmartPlaylistAsync(dto);
|
||||
}
|
||||
foreach (SmartPlaylistLinkDto playlistLink in dto.Playlists) {
|
||||
User? user = _userManager.GetUserById(playlistLink.UserId);
|
||||
if (user == null) {
|
||||
continue;
|
||||
}
|
||||
var insertItems = FilterPlaylistItems(GetAllUserMedia(user), user, dto).ToArray();
|
||||
var playlist = _playlistManager.GetPlaylists(playlistLink.UserId).Where(x => x.Id == playlistLink.PlaylistId).First();
|
||||
await ClearPlaylist(playlist);
|
||||
await _playlistManager.AddItemToPlaylistAsync(playlist.Id, insertItems, playlistLink.UserId);
|
||||
}
|
||||
var insertItems = FilterPlaylistItems(GetAllUserMedia(user), user, dto);
|
||||
Playlist playlist = playlists.First();
|
||||
await ClearPlaylist(dto, playlist, user);
|
||||
await _playlistManager.AddItemToPlaylistAsync(playlist.Id, insertItems.ToArray(), user.Id);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ClearPlaylist(SmartPlaylistDto smartPlaylist, Playlist playlist, User user) {
|
||||
private async Task ClearPlaylist(Playlist playlist) {
|
||||
// fuck if I know
|
||||
if (_libraryManager.GetItemById(playlist.Id) is not Playlist playlist_new) {
|
||||
throw new ArgumentException("");
|
||||
|
|
|
@ -1,28 +1,103 @@
|
|||
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 UserId User { 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) {
|
||||
class GuidDeserializer {
|
||||
public static Guid deserialize(string v) {
|
||||
if (v.Length == 32) {
|
||||
return Guid.ParseExact(v, "N");
|
||||
}
|
||||
return Guid.Parse(v);
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class SmartPlaylistLinkDto : ISerializable {
|
||||
public PlaylistId PlaylistId { get; set; }
|
||||
public UserId UserId { get; set; }
|
||||
|
||||
public SmartPlaylistLinkDto() {
|
||||
PlaylistId = Guid.Empty;
|
||||
UserId = Guid.Empty;
|
||||
}
|
||||
|
||||
protected SmartPlaylistLinkDto(SerializationInfo info, StreamingContext context) {
|
||||
if (info.GetValue("PlaylistId", typeof(PlaylistId)) is PlaylistId _PlaylistId) {
|
||||
PlaylistId = _PlaylistId;
|
||||
} else {
|
||||
PlaylistId = Guid.Empty;
|
||||
}
|
||||
if (info.GetValue("UserId", typeof(UserId)) is UserId _UserId) {
|
||||
UserId = _UserId;
|
||||
} else {
|
||||
UserId = Guid.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
public void GetObjectData(SerializationInfo info, StreamingContext context) {
|
||||
info.AddValue("PlaylistId", PlaylistId);
|
||||
info.AddValue("UserId", UserId);
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class SmartPlaylistDto : ISerializable {
|
||||
private static string DEFAULT_PROGRAM = "(begin (invoke item 'IsFavoriteOrLiked' (user)))";
|
||||
public SmartPlaylistId Id { get; set; }
|
||||
public SmartPlaylistLinkDto[] Playlists { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string Program { get; set; }
|
||||
public string? Filename { get; set; }
|
||||
public bool Enabled { get; set; }
|
||||
|
||||
public SmartPlaylistDto() {
|
||||
Id = Guid.NewGuid();
|
||||
Playlists = [];
|
||||
Name = Id.ToString();
|
||||
Program = DEFAULT_PROGRAM;
|
||||
Filename = null;
|
||||
Enabled = true;
|
||||
}
|
||||
|
||||
protected SmartPlaylistDto(SerializationInfo info, StreamingContext context) {
|
||||
if (info.GetValue("Id", typeof(SmartPlaylistId)) is SmartPlaylistId _Id) {
|
||||
Id = _Id;
|
||||
} else {
|
||||
Id = Guid.NewGuid();
|
||||
}
|
||||
if (Name == null) {
|
||||
if (info.GetValue("Playlists", typeof(SmartPlaylistLinkDto[])) is SmartPlaylistLinkDto[] _Playlists) {
|
||||
Playlists = _Playlists;
|
||||
} else {
|
||||
Playlists = [];
|
||||
}
|
||||
if (info.GetValue("Name", typeof(string)) is string _Name) {
|
||||
Name = _Name;
|
||||
} else {
|
||||
Name = Id.ToString();
|
||||
}
|
||||
if (Program == null) {
|
||||
if (info.GetValue("Program", typeof(string)) is string _Program) {
|
||||
Program = _Program;
|
||||
} else {
|
||||
Program = DEFAULT_PROGRAM;
|
||||
}
|
||||
if (Filename == null) {
|
||||
Filename = filename;
|
||||
}
|
||||
if (info.GetValue("Filename", typeof(string)) is string _Filename) {
|
||||
Filename = _Filename;
|
||||
} else {
|
||||
Filename = null;
|
||||
}
|
||||
if (info.GetValue("Enabled", typeof(bool)) is bool _Enabled) {
|
||||
Enabled = _Enabled;
|
||||
} else {
|
||||
Enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void GetObjectData(SerializationInfo info, StreamingContext context) {
|
||||
info.AddValue("Id", Id);
|
||||
info.AddValue("Playlists", Playlists);
|
||||
info.AddValue("Name", Name);
|
||||
info.AddValue("Program", Program);
|
||||
info.AddValue("Filename", Filename);
|
||||
info.AddValue("Enabled", Enabled);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ using System.Text.Json;
|
|||
|
||||
namespace Jellyfin.Plugin.SmartPlaylist {
|
||||
public interface IStore {
|
||||
Task<SmartPlaylistDto?> GetSmartPlaylistAsync(SmartPlaylistId smartPlaylistId);
|
||||
Task<SmartPlaylistDto> GetSmartPlaylistAsync(SmartPlaylistId smartPlaylistId);
|
||||
Task<SmartPlaylistDto[]> GetAllSmartPlaylistsAsync();
|
||||
Task SaveSmartPlaylistAsync(SmartPlaylistDto smartPlaylist);
|
||||
void DeleteSmartPlaylist(SmartPlaylistDto smartPlaylist);
|
||||
|
@ -13,13 +13,16 @@ namespace Jellyfin.Plugin.SmartPlaylist {
|
|||
public Store(ISmartPlaylistFileSystem fileSystem) {
|
||||
_fileSystem = fileSystem;
|
||||
}
|
||||
private async Task<SmartPlaylistDto?> LoadPlaylistAsync(string filename) {
|
||||
private async Task<SmartPlaylistDto> LoadPlaylistAsync(string filename) {
|
||||
await using var r = File.OpenRead(filename);
|
||||
var dto = (await JsonSerializer.DeserializeAsync<SmartPlaylistDto>(r).ConfigureAwait(false));
|
||||
dto.Fill(filename);
|
||||
if (dto == null) {
|
||||
throw new ApplicationException("");
|
||||
}
|
||||
dto.Filename = filename;
|
||||
return dto;
|
||||
}
|
||||
public async Task<SmartPlaylistDto?> GetSmartPlaylistAsync(SmartPlaylistId smartPlaylistId) {
|
||||
public async Task<SmartPlaylistDto> GetSmartPlaylistAsync(SmartPlaylistId smartPlaylistId) {
|
||||
string filename = _fileSystem.FindSmartPlaylistFilePath(smartPlaylistId);
|
||||
return await LoadPlaylistAsync(filename).ConfigureAwait(false);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
global using System;
|
||||
|
||||
global using UserId = System.Guid;
|
||||
global using PlaylistId = System.Guid;
|
||||
global using SmartPlaylistId = System.Guid;
|
||||
|
|
Loading…
Reference in a new issue