From 457755d743c5e10afc22bfd00660e5fdb59dd52b Mon Sep 17 00:00:00 2001 From: redxef Date: Thu, 16 Jan 2025 19:39:14 +0100 Subject: [PATCH 1/6] feat: add collection or playlist choice to config page and dto. Refs: #1 --- .../SmartPlaylistDto.cs | 8 +++++++ Jellyfin.Plugin.SmartPlaylist/configPage.html | 23 +++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/Jellyfin.Plugin.SmartPlaylist/SmartPlaylistDto.cs b/Jellyfin.Plugin.SmartPlaylist/SmartPlaylistDto.cs index 5a2f41b..3e914e3 100644 --- a/Jellyfin.Plugin.SmartPlaylist/SmartPlaylistDto.cs +++ b/Jellyfin.Plugin.SmartPlaylist/SmartPlaylistDto.cs @@ -50,6 +50,7 @@ namespace Jellyfin.Plugin.SmartPlaylist { public string SortProgram { get; set; } public string? Filename { get; set; } public bool Enabled { get; set; } + public bool CollectionInsteadOfPlaylist { get; set; } public SmartPlaylistDto() { Id = ""; @@ -59,6 +60,7 @@ namespace Jellyfin.Plugin.SmartPlaylist { SortProgram = DEFAULT_SORT_PROGRAM; Filename = null; Enabled = true; + CollectionInsteadOfPlaylist = false; } protected SmartPlaylistDto(SerializationInfo info, StreamingContext context) { @@ -97,6 +99,11 @@ namespace Jellyfin.Plugin.SmartPlaylist { } else { Enabled = true; } + if (info.GetValue("CollectionInsteadOfPlaylist", typeof(bool)) is bool _CollectionInsteadOfPlaylist) { + Enabled = _CollectionInsteadOfPlaylist; + } else { + Enabled = true; + } } public void GetObjectData(SerializationInfo info, StreamingContext context) { @@ -106,6 +113,7 @@ namespace Jellyfin.Plugin.SmartPlaylist { info.AddValue("Program", Program); info.AddValue("Filename", Filename); info.AddValue("Enabled", Enabled); + info.AddValue("CollectionInsteadOfPlaylist", CollectionInsteadOfPlaylist); } } } diff --git a/Jellyfin.Plugin.SmartPlaylist/configPage.html b/Jellyfin.Plugin.SmartPlaylist/configPage.html index cd597d2..36dce3b 100644 --- a/Jellyfin.Plugin.SmartPlaylist/configPage.html +++ b/Jellyfin.Plugin.SmartPlaylist/configPage.html @@ -44,6 +44,19 @@
Is the playlist enabled.
+
+
+ Should a playlist or a collection be generated: +
+ + +
+
+ + +
+
+
-
-
- Should a playlist or a collection be generated: -
- - -
-
- - -
-
-
+
+ + + + + + + diff --git a/Jellyfin.Plugin.SmartPlaylist/Configuration/configPage.js b/Jellyfin.Plugin.SmartPlaylist/Configuration/configPage.js new file mode 100644 index 0000000..70fb8be --- /dev/null +++ b/Jellyfin.Plugin.SmartPlaylist/Configuration/configPage.js @@ -0,0 +1,23 @@ +var SmartPlaylistConfig = { + pluginUniqueId: 'dd2326e3-4d3e-4bfc-80e6-28502c1131df' +}; + +document.querySelector('#SmartPlaylistConfigPage') + .addEventListener('pageshow', function() { + Dashboard.showLoadingMsg(); + ApiClient.getPluginConfiguration(SmartPlaylistConfig.pluginUniqueId).then(function (config) { + document.querySelector('#InitialProgram').value = config.InitialProgram; + Dashboard.hideLoadingMsg(); + }); + }); + +document.querySelector('#SmartPlaylistConfigForm') + .addEventListener('submit', function(e) { + Dashboard.showLoadingMsg(); + ApiClient.getPluginConfiguration(SmartPlaylistConfig.pluginUniqueId).then(function (config) { + config.InitialProgram = document.querySelector('#InitialProgram').value; + }); + e.preventDefault(); + return false; +}); + diff --git a/Jellyfin.Plugin.SmartPlaylist/Pages/smartPlaylists.html b/Jellyfin.Plugin.SmartPlaylist/Pages/smartPlaylists.html new file mode 100644 index 0000000..2d93b39 --- /dev/null +++ b/Jellyfin.Plugin.SmartPlaylist/Pages/smartPlaylists.html @@ -0,0 +1,57 @@ + + + + + SmartPlaylist + + +
+
+
+
+
+ + +
+
+ + +
+
+ +
A program which should return t or nil to include or exclude the provided item.
+ +
+
+ +
A program which should return a list of items to include in the playlist, sorted however you like.
+ +
+
+ +
Which users should get access to the playlist.
+ +
+
+ +
Is the playlist enabled.
+ +
+
+ +
+
+
+
+ +
+ + diff --git a/Jellyfin.Plugin.SmartPlaylist/Pages/smartPlaylists.js b/Jellyfin.Plugin.SmartPlaylist/Pages/smartPlaylists.js new file mode 100644 index 0000000..a0fb77b --- /dev/null +++ b/Jellyfin.Plugin.SmartPlaylist/Pages/smartPlaylists.js @@ -0,0 +1,144 @@ + +var PLAYLISTS = [ + { + Id: 'My New Smartplaylist', + Name: 'My New Smartplaylist', + Program: '(is-favourite)', + SortProgram: '(begin *items*)', + Playlists: [], + Enabled: true, + } +]; +var USERS = []; + +function fillForm(playlist, users) { + const editName = document.querySelector('#SmartplaylistEditName'); + const editProgram = document.querySelector('#SmartplaylistEditProgram'); + const editSortProgram = document.querySelector('#SmartplaylistEditSortProgram'); + const editUsers = document.querySelector('#SmartplaylistEditUsers'); + const editEnabled = document.querySelector('#SmartplaylistEditEnabled'); + editName.value = playlist.Name; + editProgram.value = playlist.Program; + editSortProgram.value = playlist.SortProgram; + editUsers.innerHTML = ''; + + var added_users = [] + for (const p of playlist.Playlists) { + var o = document.createElement('option'); + o.value = btoa(JSON.stringify(p)); + o.innerHTML = users.filter(u => u.Id == p.UserId).map(u => u.Name)[0]; + o.setAttribute('selected', 'selected'); + editUsers.appendChild(o); + added_users.push(p.UserId); + } + for (const u of users) { + if (added_users.includes(u.Id)) { + continue; + } + var o = document.createElement('option'); + o.value = btoa(JSON.stringify({ + 'PlaylistId': '00000000000000000000000000000000', + 'UserId': u.Id, + })); + o.innerHTML = u.Name; + editUsers.appendChild(o); + } + editEnabled.checked = playlist.Enabled; +} + +function fillPlaylistSelect(playlists) { + const selection = document.querySelector('#SmartplaylistSelection'); + selection.innerHTML = ''; + var o = document.createElement('option'); + o.value = null; + o.innerHTML = 'Create new playlist ...'; + selection.appendChild(o); + for (const i of playlists.slice(1)) { + var o = document.createElement('option'); + o.value = i.Id; + o.innerHTML = i.Name; + selection.appendChild(o); + } +} + +function jsonFromForm() { + const editName = document.querySelector('#SmartplaylistEditName'); + const editProgram = document.querySelector('#SmartplaylistEditProgram'); + const editSortProgram = document.querySelector('#SmartplaylistEditSortProgram'); + const editUsers = document.querySelector('#SmartplaylistEditUsers'); + const editEnabled = document.querySelector('#SmartplaylistEditEnabled'); + return { + Id: editName.value, + Name: editName.value, + Program: editProgram.value, + SortProgram: editSortProgram.value, + Playlists: Array.from(editUsers.options).filter((x) => x.selected).map(x => JSON.parse(atob(x.value))), + Enabled: editEnabled.checked, + }; +} + +ApiClient.getSmartPlaylists = function () { + const url = ApiClient.getUrl('SmartPlaylist'); + return this.ajax({ + type: 'GET', + url: url, + dataType: 'json', + }); +} + +ApiClient.setSmartPlaylist = function (p) { + const url = ApiClient.getUrl('SmartPlaylist'); + return this.ajax({ + type: 'POST', + url: url, + dataType: 'json', + contentType: 'application/json; charset=UTF-8', + data: JSON.stringify(p), + }); +} + +document.querySelector('#SmartPlaylistConfigPage') + .addEventListener('pageshow', function() { + Dashboard.showLoadingMsg(); + ApiClient.getSmartPlaylists().then(function (playlists) { + PLAYLISTS = [PLAYLISTS[0]].concat(playlists); + ApiClient.getUsers().then(function (users) { + USERS = users; + const selection = document.querySelector('#SmartplaylistSelection'); + fillPlaylistSelect(PLAYLISTS); + console.log('selectedIndex =', selection.selectedIndex); + console.log('selectedIndex =', PLAYLISTS[selection.selectedIndex]); + fillForm(PLAYLISTS[selection.selectedIndex], USERS); + Dashboard.hideLoadingMsg(); + }); + }); + }); + +document.querySelector('#SmartplaylistSelection') + .addEventListener('change', function() { + const selection = document.querySelector('#SmartplaylistSelection'); + console.log('p =', PLAYLISTS[selection.selectedIndex]); + fillForm(PLAYLISTS[selection.selectedIndex], USERS); + }); + +document.querySelector('#SmartPlaylistConfigForm') + .addEventListener('submit', function (e) { + Dashboard.showLoadingMsg(); + const selection = document.querySelector('#SmartplaylistSelection'); + const selectedid = PLAYLISTS[selection.selectedIndex].Id; + ApiClient.setSmartPlaylist(jsonFromForm()).then(function () { + ApiClient.getSmartPlaylists().then(function (playlists) { + PLAYLISTS = [PLAYLISTS[0]].concat(playlists); + ApiClient.getUsers().then(function (users) { + USERS = users; + fillPlaylistSelect(PLAYLISTS); + const idx = PLAYLISTS.map(x => x.Id).indexOf(selectedid); + selection.selectedIndex = idx; + fillForm(PLAYLISTS[selection.selectedIndex], USERS); + Dashboard.hideLoadingMsg(); + }); + }); + }); + e.preventDefault(); + }) + diff --git a/Jellyfin.Plugin.SmartPlaylist/Plugin.cs b/Jellyfin.Plugin.SmartPlaylist/Plugin.cs index 72d7763..12046e3 100644 --- a/Jellyfin.Plugin.SmartPlaylist/Plugin.cs +++ b/Jellyfin.Plugin.SmartPlaylist/Plugin.cs @@ -27,8 +27,30 @@ namespace Jellyfin.Plugin.SmartPlaylist { return new[] { new PluginPageInfo { Name = this.Name, - EmbeddedResourcePath = string.Format(CultureInfo.InvariantCulture, "{0}.configPage.html", GetType().Namespace) - } + EmbeddedResourcePath = string.Format(CultureInfo.InvariantCulture, "{0}.Configuration.configPage.html", GetType().Namespace), + }, + new PluginPageInfo { + Name = "configPage.js", + EmbeddedResourcePath = string.Format(CultureInfo.InvariantCulture, "{0}.Configuration.configPage.js", GetType().Namespace), + }, + new PluginPageInfo { + Name = "Smart Playlists", + EmbeddedResourcePath = string.Format(CultureInfo.InvariantCulture, "{0}.Pages.smartPlaylists.html", GetType().Namespace), + EnableInMainMenu = true, + }, + new PluginPageInfo { + Name = "smartPlaylists.js", + EmbeddedResourcePath = string.Format(CultureInfo.InvariantCulture, "{0}.Pages.smartPlaylists.js", GetType().Namespace), + }, + new PluginPageInfo { + Name = "Smart Collections", + EmbeddedResourcePath = string.Format(CultureInfo.InvariantCulture, "{0}.Pages.smartCollections.html", GetType().Namespace), + //EnableInMainMenu = true, + }, + new PluginPageInfo { + Name = "smartCollections.js", + EmbeddedResourcePath = string.Format(CultureInfo.InvariantCulture, "{0}.Pages.smartCollections.js", GetType().Namespace), + }, }; } } diff --git a/Jellyfin.Plugin.SmartPlaylist/ScheduledTasks/GenerateCollection.cs b/Jellyfin.Plugin.SmartPlaylist/ScheduledTasks/GenerateCollection.cs index 480de70..fb672a5 100644 --- a/Jellyfin.Plugin.SmartPlaylist/ScheduledTasks/GenerateCollection.cs +++ b/Jellyfin.Plugin.SmartPlaylist/ScheduledTasks/GenerateCollection.cs @@ -1,4 +1,3 @@ -using System.Globalization; using MediaBrowser.Model.Tasks; using Microsoft.Extensions.Logging; using MediaBrowser.Controller; @@ -6,12 +5,8 @@ using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Collections; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.IO; -using Jellyfin.Data.Entities; -using Jellyfin.Data.Enums; -using MediaBrowser.Model.Entities; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Movies; -using MediaBrowser.Model.Collections; namespace Jellyfin.Plugin.SmartPlaylist.ScheduledTasks { diff --git a/Jellyfin.Plugin.SmartPlaylist/ScheduledTasks/GeneratePlaylist.cs b/Jellyfin.Plugin.SmartPlaylist/ScheduledTasks/GeneratePlaylist.cs index ed79e98..6029ac9 100644 --- a/Jellyfin.Plugin.SmartPlaylist/ScheduledTasks/GeneratePlaylist.cs +++ b/Jellyfin.Plugin.SmartPlaylist/ScheduledTasks/GeneratePlaylist.cs @@ -7,15 +7,12 @@ using MediaBrowser.Controller.Playlists; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.IO; using Jellyfin.Data.Entities; -using Jellyfin.Data.Enums; using MediaBrowser.Model.Entities; using MediaBrowser.Controller.Entities; using MediaBrowser.Model.Playlists; using Jellyfin.Plugin.SmartPlaylist.Lisp; using Jellyfin.Plugin.SmartPlaylist.Lisp.Compiler; -using Lisp_Object = Jellyfin.Plugin.SmartPlaylist.Lisp.Object; -using Lisp_Boolean = Jellyfin.Plugin.SmartPlaylist.Lisp.Boolean; namespace Jellyfin.Plugin.SmartPlaylist.ScheduledTasks { @@ -78,30 +75,30 @@ namespace Jellyfin.Plugin.SmartPlaylist.ScheduledTasks { Expression expression = new Parser(StringTokenStream.generate(smartPlaylist.Program)).parse(); // parse here, so that we don't repeat the work for each item Executor executor = SetupExecutor(); - executor.environment.Set("*user*", Lisp_Object.FromBase(user)); + executor.environment.Set("*user*", Lisp.Object.FromBase(user)); foreach (var i in items) { - executor.environment.Set("*item*", Lisp_Object.FromBase(i)); + executor.environment.Set("*item*", Lisp.Object.FromBase(i)); var r = executor.eval(expression); _logger.LogTrace("Item {0} evaluated to {1}", i, r.ToString()); - if ((r is not Lisp_Boolean r_bool) || (r_bool.Value())) { + if ((r is not Lisp.Boolean r_bool) || (r_bool.Value())) { _logger.LogDebug("Added '{0}' to Smart Playlist {1}", i, smartPlaylist.Name); results.Add(i); } } executor = SetupExecutor(); - executor.environment.Set("*user*", Lisp_Object.FromBase(user)); - executor.environment.Set("*items*", Lisp_Object.FromBase(results)); + executor.environment.Set("*user*", Lisp.Object.FromBase(user)); + executor.environment.Set("*items*", Lisp.Object.FromBase(results)); results = new List(); var sort_result = executor.eval(smartPlaylist.SortProgram); if (sort_result is Cons sorted_items) { foreach (var i in sorted_items.ToList()) { - if (i is Lisp_Object iObject && iObject.Value() is BaseItem iBaseItem) { + if (i is Lisp.Object iObject && iObject.Value() is BaseItem iBaseItem) { results.Add(iBaseItem); continue; } throw new ApplicationException($"Returned sorted list does contain unexpected items, got {i}"); } - } else if (sort_result == Lisp_Boolean.FALSE) { + } else if (sort_result == Lisp.Boolean.FALSE) { } else { throw new ApplicationException($"Did not return a list of items, returned {sort_result}"); } diff --git a/Jellyfin.Plugin.SmartPlaylist/configPage.html b/Jellyfin.Plugin.SmartPlaylist/configPage.html deleted file mode 100644 index cd597d2..0000000 --- a/Jellyfin.Plugin.SmartPlaylist/configPage.html +++ /dev/null @@ -1,201 +0,0 @@ - - - - - SmartPlaylist - - -
-
-
-
-
- -
A program which can set up the environment
- -
-
- - -
-
- - -
-
- -
A program which should return t or nil to include or exclude the provided item.
- -
-
- -
A program which should return a list of items to include in the playlist, sorted however you like.
- -
-
- -
Which users should get access to the playlist.
- -
-
- -
Is the playlist enabled.
- -
-
- -
-
-
-
- - -
- - diff --git a/Jellyfin.Plugin.SmartPlaylist/jellyfin-smart-playlist.csproj b/Jellyfin.Plugin.SmartPlaylist/jellyfin-smart-playlist.csproj index a35388f..85efc5b 100644 --- a/Jellyfin.Plugin.SmartPlaylist/jellyfin-smart-playlist.csproj +++ b/Jellyfin.Plugin.SmartPlaylist/jellyfin-smart-playlist.csproj @@ -15,8 +15,18 @@ - - + + + + + + + + + + + + From aa6fed146db1f71e4c584a7c957781d7ccc39b8a Mon Sep 17 00:00:00 2001 From: redxef Date: Sun, 19 Jan 2025 22:28:32 +0100 Subject: [PATCH 5/6] feat: enable collections. --- .../Api/SmartCollectionController.cs | 2 +- .../Lisp/Interpreter.cs | 2 +- .../Pages/smartCollections.html | 51 ++++++++ .../Pages/smartCollections.js | 111 ++++++++++++++++++ .../Pages/smartPlaylists.html | 2 +- .../Pages/smartPlaylists.js | 8 +- Jellyfin.Plugin.SmartPlaylist/Plugin.cs | 4 +- .../ScheduledTasks/GenerateCollection.cs | 4 +- 8 files changed, 172 insertions(+), 12 deletions(-) create mode 100644 Jellyfin.Plugin.SmartPlaylist/Pages/smartCollections.html create mode 100644 Jellyfin.Plugin.SmartPlaylist/Pages/smartCollections.js diff --git a/Jellyfin.Plugin.SmartPlaylist/Api/SmartCollectionController.cs b/Jellyfin.Plugin.SmartPlaylist/Api/SmartCollectionController.cs index 4a597a1..da0f62d 100644 --- a/Jellyfin.Plugin.SmartPlaylist/Api/SmartCollectionController.cs +++ b/Jellyfin.Plugin.SmartPlaylist/Api/SmartCollectionController.cs @@ -54,7 +54,7 @@ namespace Jellyfin.Plugin.SmartPlaylist.Api { [HttpPost] [ProducesResponseType(StatusCodes.Status201Created)] - public async Task SetCollection([FromRoute, Required] CollectionId collectionId, [FromBody] SmartCollectionDto smartCollection) { + public async Task SetCollection([FromBody, Required] SmartCollectionDto smartCollection) { await _store.SaveSmartCollectionAsync(smartCollection); return Created(); } diff --git a/Jellyfin.Plugin.SmartPlaylist/Lisp/Interpreter.cs b/Jellyfin.Plugin.SmartPlaylist/Lisp/Interpreter.cs index 7027c09..c713d79 100644 --- a/Jellyfin.Plugin.SmartPlaylist/Lisp/Interpreter.cs +++ b/Jellyfin.Plugin.SmartPlaylist/Lisp/Interpreter.cs @@ -279,7 +279,7 @@ namespace Jellyfin.Plugin.SmartPlaylist.Lisp { IList r = new List(); MethodInfo? mi = o.Value().GetType().GetMethod(s.Value(), l_types); if (mi == null) { - throw new ApplicationException($"{o.Value()} has no method {s.Value()}"); + throw new ApplicationException($"{o.Value()} ({o.Value().GetType()}) has no method {s.Value()}"); } return Object.FromBase(mi.Invoke(o.Value(), l_)); diff --git a/Jellyfin.Plugin.SmartPlaylist/Pages/smartCollections.html b/Jellyfin.Plugin.SmartPlaylist/Pages/smartCollections.html new file mode 100644 index 0000000..15c50c6 --- /dev/null +++ b/Jellyfin.Plugin.SmartPlaylist/Pages/smartCollections.html @@ -0,0 +1,51 @@ + + + + + SmartCollection + + +
+
+
+
+
+ + +
+
+ + +
+
+ +
A program which should return t or nil to include or exclude the provided item.
+ +
+
+ +
A program which should return a list of items to include in the collection, sorted however you like.
+ +
+
+ +
Is the collection enabled.
+ +
+
+ +
+
+
+
+ +
+ + diff --git a/Jellyfin.Plugin.SmartPlaylist/Pages/smartCollections.js b/Jellyfin.Plugin.SmartPlaylist/Pages/smartCollections.js new file mode 100644 index 0000000..8b9aadb --- /dev/null +++ b/Jellyfin.Plugin.SmartPlaylist/Pages/smartCollections.js @@ -0,0 +1,111 @@ + +var COLLECTIONS = [ + { + Id: 'My New Smartcollection', + Name: 'My New Smartcollection', + Program: '(is-favourite)', + SortProgram: '(begin *items*)', + CollectionId: '00000000000000000000000000000000', + Enabled: true, + } +]; + +function fillForm(collection) { + const editName = document.querySelector('#SmartcollectionEditName'); + const editProgram = document.querySelector('#SmartcollectionEditProgram'); + const editSortProgram = document.querySelector('#SmartcollectionEditSortProgram'); + const editEnabled = document.querySelector('#SmartcollectionEditEnabled'); + editName.value = collection.Name; + editProgram.value = collection.Program; + editSortProgram.value = collection.SortProgram; + editEnabled.checked = collection.Enabled; +} + +function fillCollectionSelect(collections) { + const selection = document.querySelector('#SmartcollectionSelection'); + selection.innerHTML = ''; + var o = document.createElement('option'); + o.value = null; + o.innerHTML = 'Create new collection ...'; + selection.appendChild(o); + for (const i of collections.slice(1)) { + var o = document.createElement('option'); + o.value = i.Id; + o.innerHTML = i.Name; + selection.appendChild(o); + } +} + +function jsonFromForm(collectionId) { + const editName = document.querySelector('#SmartcollectionEditName'); + const editProgram = document.querySelector('#SmartcollectionEditProgram'); + const editSortProgram = document.querySelector('#SmartcollectionEditSortProgram'); + const editEnabled = document.querySelector('#SmartcollectionEditEnabled'); + if (collectionId === null) { + collectionId = '00000000000000000000000000000000' + } + return { + Id: editName.value, + Name: editName.value, + Program: editProgram.value, + SortProgram: editSortProgram.value, + CollectionId: collectionId, + Enabled: editEnabled.checked, + }; +} + +ApiClient.getSmartCollections = function () { + const url = ApiClient.getUrl('SmartCollection'); + return this.ajax({ + type: 'GET', + url: url, + dataType: 'json', + }); +} + +ApiClient.setSmartCollection = function (c) { + const url = ApiClient.getUrl('SmartCollection'); + return this.ajax({ + type: 'POST', + url: url, + dataType: 'json', + contentType: 'application/json; charset=UTF-8', + data: JSON.stringify(c), + }); +} + +document.querySelector('#SmartCollectionConfigPage') + .addEventListener('viewshow', function() { + Dashboard.showLoadingMsg(); + ApiClient.getSmartCollections().then(function (collections) { + COLLECTIONS = [COLLECTIONS[0]].concat(collections); + const selection = document.querySelector('#SmartcollectionSelection'); + fillCollectionSelect(COLLECTIONS); + fillForm(COLLECTIONS[selection.selectedIndex]); + Dashboard.hideLoadingMsg(); + }); + }); + +document.querySelector('#SmartcollectionSelection') + .addEventListener('change', function() { + const selection = document.querySelector('#SmartcollectionSelection'); + fillForm(COLLECTIONS[selection.selectedIndex]); + }); + +document.querySelector('#SmartCollectionConfigForm') + .addEventListener('submit', function (e) { + Dashboard.showLoadingMsg(); + const selection = document.querySelector('#SmartcollectionSelection'); + const selectedid = COLLECTIONS[selection.selectedIndex].Id; + ApiClient.setSmartCollection(jsonFromForm(COLLECTIONS[selection.selectedIndex].CollectionId)).then(function () { + ApiClient.getSmartCollections().then(function (collections) { + COLLECTIONS = [COLLECTIONS[0]].concat(collections); + fillCollectionSelect(COLLECTIONS); + const idx = COLLECTIONS.map(x => x.Id).indexOf(selectedid); + selection.selectedIndex = idx; + fillForm(COLLECTIONS[selection.selectedIndex]); + Dashboard.hideLoadingMsg(); + }); + }); + e.preventDefault(); + }); diff --git a/Jellyfin.Plugin.SmartPlaylist/Pages/smartPlaylists.html b/Jellyfin.Plugin.SmartPlaylist/Pages/smartPlaylists.html index 2d93b39..5720218 100644 --- a/Jellyfin.Plugin.SmartPlaylist/Pages/smartPlaylists.html +++ b/Jellyfin.Plugin.SmartPlaylist/Pages/smartPlaylists.html @@ -35,7 +35,7 @@
- +
Is the playlist enabled.
diff --git a/Jellyfin.Plugin.SmartPlaylist/Pages/smartPlaylists.js b/Jellyfin.Plugin.SmartPlaylist/Pages/smartPlaylists.js index a0fb77b..7a0d677 100644 --- a/Jellyfin.Plugin.SmartPlaylist/Pages/smartPlaylists.js +++ b/Jellyfin.Plugin.SmartPlaylist/Pages/smartPlaylists.js @@ -98,7 +98,7 @@ ApiClient.setSmartPlaylist = function (p) { } document.querySelector('#SmartPlaylistConfigPage') - .addEventListener('pageshow', function() { + .addEventListener('viewshow', function() { Dashboard.showLoadingMsg(); ApiClient.getSmartPlaylists().then(function (playlists) { PLAYLISTS = [PLAYLISTS[0]].concat(playlists); @@ -106,8 +106,6 @@ document.querySelector('#SmartPlaylistConfigPage') USERS = users; const selection = document.querySelector('#SmartplaylistSelection'); fillPlaylistSelect(PLAYLISTS); - console.log('selectedIndex =', selection.selectedIndex); - console.log('selectedIndex =', PLAYLISTS[selection.selectedIndex]); fillForm(PLAYLISTS[selection.selectedIndex], USERS); Dashboard.hideLoadingMsg(); }); @@ -117,7 +115,6 @@ document.querySelector('#SmartPlaylistConfigPage') document.querySelector('#SmartplaylistSelection') .addEventListener('change', function() { const selection = document.querySelector('#SmartplaylistSelection'); - console.log('p =', PLAYLISTS[selection.selectedIndex]); fillForm(PLAYLISTS[selection.selectedIndex], USERS); }); @@ -140,5 +137,4 @@ document.querySelector('#SmartPlaylistConfigForm') }); }); e.preventDefault(); - }) - + }); diff --git a/Jellyfin.Plugin.SmartPlaylist/Plugin.cs b/Jellyfin.Plugin.SmartPlaylist/Plugin.cs index 12046e3..0d962f6 100644 --- a/Jellyfin.Plugin.SmartPlaylist/Plugin.cs +++ b/Jellyfin.Plugin.SmartPlaylist/Plugin.cs @@ -35,6 +35,7 @@ namespace Jellyfin.Plugin.SmartPlaylist { }, new PluginPageInfo { Name = "Smart Playlists", + DisplayName = "Smart Playlists", EmbeddedResourcePath = string.Format(CultureInfo.InvariantCulture, "{0}.Pages.smartPlaylists.html", GetType().Namespace), EnableInMainMenu = true, }, @@ -44,8 +45,9 @@ namespace Jellyfin.Plugin.SmartPlaylist { }, new PluginPageInfo { Name = "Smart Collections", + DisplayName = "Smart Collections", EmbeddedResourcePath = string.Format(CultureInfo.InvariantCulture, "{0}.Pages.smartCollections.html", GetType().Namespace), - //EnableInMainMenu = true, + EnableInMainMenu = true, }, new PluginPageInfo { Name = "smartCollections.js", diff --git a/Jellyfin.Plugin.SmartPlaylist/ScheduledTasks/GenerateCollection.cs b/Jellyfin.Plugin.SmartPlaylist/ScheduledTasks/GenerateCollection.cs index fb672a5..d450d5b 100644 --- a/Jellyfin.Plugin.SmartPlaylist/ScheduledTasks/GenerateCollection.cs +++ b/Jellyfin.Plugin.SmartPlaylist/ScheduledTasks/GenerateCollection.cs @@ -101,8 +101,8 @@ namespace Jellyfin.Plugin.SmartPlaylist.ScheduledTasks { if (_libraryManager.GetItemById(collectionId) is not BoxSet collection) { throw new ArgumentException(""); } - var existingItems = collection.Children; - await _collectionManager.RemoveFromCollectionAsync(collectionId, existingItems.Select(x => x.Id)); + var existingItems = collection.LinkedChildren.Select(x => x.ItemId).Where(x => x != null).Select(x => x.Value); + await _collectionManager.RemoveFromCollectionAsync(collectionId, existingItems); } } } From 9ca1712b5d5c16708e8bd719f4260ef0831f4815 Mon Sep 17 00:00:00 2001 From: redxef Date: Sun, 19 Jan 2025 22:39:24 +0100 Subject: [PATCH 6/6] fix: loading of the config pages. --- .../Pages/smartCollections.js | 43 +++++++++------- .../Pages/smartPlaylists.js | 49 ++++++++++--------- 2 files changed, 50 insertions(+), 42 deletions(-) diff --git a/Jellyfin.Plugin.SmartPlaylist/Pages/smartCollections.js b/Jellyfin.Plugin.SmartPlaylist/Pages/smartCollections.js index 8b9aadb..db0ec34 100644 --- a/Jellyfin.Plugin.SmartPlaylist/Pages/smartCollections.js +++ b/Jellyfin.Plugin.SmartPlaylist/Pages/smartCollections.js @@ -3,7 +3,7 @@ var COLLECTIONS = [ { Id: 'My New Smartcollection', Name: 'My New Smartcollection', - Program: '(is-favourite)', + Program: 'nil', SortProgram: '(begin *items*)', CollectionId: '00000000000000000000000000000000', Enabled: true, @@ -73,17 +73,29 @@ ApiClient.setSmartCollection = function (c) { data: JSON.stringify(c), }); } - +function initial_load(selectedId) { + Dashboard.showLoadingMsg(); + ApiClient.getSmartCollections().then(function (collections) { + COLLECTIONS = [COLLECTIONS[0]].concat(collections); + const selection = document.querySelector('#SmartcollectionSelection'); + fillCollectionSelect(COLLECTIONS); + if (selectedId === null) { + selection.selectedIndex = 0; + } else { + selection.selectedIndex = COLLECTIONS.map(x => x.Id).indexOf(selectedId); + } + fillForm(COLLECTIONS[selection.selectedIndex]); + Dashboard.hideLoadingMsg(); + }); +} document.querySelector('#SmartCollectionConfigPage') .addEventListener('viewshow', function() { - Dashboard.showLoadingMsg(); - ApiClient.getSmartCollections().then(function (collections) { - COLLECTIONS = [COLLECTIONS[0]].concat(collections); - const selection = document.querySelector('#SmartcollectionSelection'); - fillCollectionSelect(COLLECTIONS); - fillForm(COLLECTIONS[selection.selectedIndex]); - Dashboard.hideLoadingMsg(); - }); + initial_load(null); + }); + +document.querySelector('#SmartCollectionConfigPage') + .addEventListener('pageshow', function() { + initial_load(null); }); document.querySelector('#SmartcollectionSelection') @@ -96,16 +108,9 @@ document.querySelector('#SmartCollectionConfigForm') .addEventListener('submit', function (e) { Dashboard.showLoadingMsg(); const selection = document.querySelector('#SmartcollectionSelection'); - const selectedid = COLLECTIONS[selection.selectedIndex].Id; + const selectedId = COLLECTIONS[selection.selectedIndex].Id; ApiClient.setSmartCollection(jsonFromForm(COLLECTIONS[selection.selectedIndex].CollectionId)).then(function () { - ApiClient.getSmartCollections().then(function (collections) { - COLLECTIONS = [COLLECTIONS[0]].concat(collections); - fillCollectionSelect(COLLECTIONS); - const idx = COLLECTIONS.map(x => x.Id).indexOf(selectedid); - selection.selectedIndex = idx; - fillForm(COLLECTIONS[selection.selectedIndex]); - Dashboard.hideLoadingMsg(); - }); + initial_load(selectedId); }); e.preventDefault(); }); diff --git a/Jellyfin.Plugin.SmartPlaylist/Pages/smartPlaylists.js b/Jellyfin.Plugin.SmartPlaylist/Pages/smartPlaylists.js index 7a0d677..0eda8ed 100644 --- a/Jellyfin.Plugin.SmartPlaylist/Pages/smartPlaylists.js +++ b/Jellyfin.Plugin.SmartPlaylist/Pages/smartPlaylists.js @@ -97,19 +97,32 @@ ApiClient.setSmartPlaylist = function (p) { }); } +function initial_load(selectedId) { + Dashboard.showLoadingMsg(); + ApiClient.getSmartPlaylists().then(function (playlists) { + PLAYLISTS = [PLAYLISTS[0]].concat(playlists); + ApiClient.getUsers().then(function (users) { + USERS = users; + const selection = document.querySelector('#SmartplaylistSelection'); + fillPlaylistSelect(PLAYLISTS); + if (selectedId === null) { + selection.selectedIndex = 0; + } else { + selection.selectedIndex = PLAYLISTS.map(x => x.Id).indexOf(selectedId); + } + fillForm(PLAYLISTS[selection.selectedIndex], USERS); + Dashboard.hideLoadingMsg(); + }); + }); +} + document.querySelector('#SmartPlaylistConfigPage') .addEventListener('viewshow', function() { - Dashboard.showLoadingMsg(); - ApiClient.getSmartPlaylists().then(function (playlists) { - PLAYLISTS = [PLAYLISTS[0]].concat(playlists); - ApiClient.getUsers().then(function (users) { - USERS = users; - const selection = document.querySelector('#SmartplaylistSelection'); - fillPlaylistSelect(PLAYLISTS); - fillForm(PLAYLISTS[selection.selectedIndex], USERS); - Dashboard.hideLoadingMsg(); - }); - }); + initial_load(null); + }); +document.querySelector('#SmartPlaylistConfigPage') + .addEventListener('pageshow', function() { + initial_load(null); }); document.querySelector('#SmartplaylistSelection') @@ -122,19 +135,9 @@ document.querySelector('#SmartPlaylistConfigForm') .addEventListener('submit', function (e) { Dashboard.showLoadingMsg(); const selection = document.querySelector('#SmartplaylistSelection'); - const selectedid = PLAYLISTS[selection.selectedIndex].Id; + const selectedId = PLAYLISTS[selection.selectedIndex].Id; ApiClient.setSmartPlaylist(jsonFromForm()).then(function () { - ApiClient.getSmartPlaylists().then(function (playlists) { - PLAYLISTS = [PLAYLISTS[0]].concat(playlists); - ApiClient.getUsers().then(function (users) { - USERS = users; - fillPlaylistSelect(PLAYLISTS); - const idx = PLAYLISTS.map(x => x.Id).indexOf(selectedid); - selection.selectedIndex = idx; - fillForm(PLAYLISTS[selection.selectedIndex], USERS); - Dashboard.hideLoadingMsg(); - }); - }); + initial_load(selectedId); }); e.preventDefault(); });