feat: enable collections.
This commit is contained in:
parent
6ac75835f0
commit
aa6fed146d
8 changed files with 172 additions and 12 deletions
|
@ -54,7 +54,7 @@ namespace Jellyfin.Plugin.SmartPlaylist.Api {
|
||||||
|
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
[ProducesResponseType(StatusCodes.Status201Created)]
|
[ProducesResponseType(StatusCodes.Status201Created)]
|
||||||
public async Task<ActionResult> SetCollection([FromRoute, Required] CollectionId collectionId, [FromBody] SmartCollectionDto smartCollection) {
|
public async Task<ActionResult> SetCollection([FromBody, Required] SmartCollectionDto smartCollection) {
|
||||||
await _store.SaveSmartCollectionAsync(smartCollection);
|
await _store.SaveSmartCollectionAsync(smartCollection);
|
||||||
return Created();
|
return Created();
|
||||||
}
|
}
|
||||||
|
|
|
@ -279,7 +279,7 @@ namespace Jellyfin.Plugin.SmartPlaylist.Lisp {
|
||||||
IList<Expression> r = new List<Expression>();
|
IList<Expression> r = new List<Expression>();
|
||||||
MethodInfo? mi = o.Value().GetType().GetMethod(s.Value(), l_types);
|
MethodInfo? mi = o.Value().GetType().GetMethod(s.Value(), l_types);
|
||||||
if (mi == null) {
|
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_));
|
return Object.FromBase(mi.Invoke(o.Value(), l_));
|
||||||
|
|
51
Jellyfin.Plugin.SmartPlaylist/Pages/smartCollections.html
Normal file
51
Jellyfin.Plugin.SmartPlaylist/Pages/smartCollections.html
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>SmartCollection</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="SmartCollectionConfigPage" data-role="page" class="page type-interior pluginConfigurationPage" data-require="emby-input,emby-button,emby-select,emby-checkbox" data-controller="__plugin/smartCollections.js">
|
||||||
|
<div data-role="content">
|
||||||
|
<div class="content-primary">
|
||||||
|
<form id="SmartCollectionConfigForm">
|
||||||
|
<div>
|
||||||
|
<label class="inputLabel inputLabelUnfocused" for="SmartcollectionSelection">Choose a collection to edit</label>
|
||||||
|
<select id="SmartcollectionSelection" class="emby-select">
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="inputLabel inputLabelUnfocused" for="SmartcollectionEditName">Name</label>
|
||||||
|
<input id="SmartcollectionEditName" type="text" class="emby-input"/>
|
||||||
|
</div>
|
||||||
|
<div class="inputContainer">
|
||||||
|
<label class="inputLabel inputLabelUnfocused" for="SmartcollectionEditProgram">Program</label>
|
||||||
|
<div class="fieldDescription">A program which should return <code>t</code> or <code>nil</code> to include or exclude the provided <code>item</code>.</div>
|
||||||
|
<textarea id="SmartcollectionEditProgram" class="emby-input smartcollection-monospace" name="Program" rows="16" cols="120"></textarea>
|
||||||
|
</div>
|
||||||
|
<div class="inputContainer">
|
||||||
|
<label class="inputLabel inputLabelUnfocused" for="SmartcollectionEditSortProgram">Sort Program</label>
|
||||||
|
<div class="fieldDescription">A program which should return a list of items to include in the collection, sorted however you like.</div>
|
||||||
|
<textarea id="SmartcollectionEditSortProgram" class="emby-input smartcollection-monospace" name="SortProgram" rows="16" cols="120"></textarea>
|
||||||
|
</div>
|
||||||
|
<div class="inputContainer">
|
||||||
|
<label class="inputLabel inputLabelUnfocused" for="SmartcollectionEditEnabled">Enabled</label>
|
||||||
|
<div class="fieldDescription">Is the collection enabled.</div>
|
||||||
|
<input id="SmartcollectionEditEnabled" type="checkbox" class="emby-input"/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<button is="emby-button" type="submit" class="raised button-submit block emby-button">
|
||||||
|
<span>Save</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<style>
|
||||||
|
.smartcollection-monospace {
|
||||||
|
font-family: monospace;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
111
Jellyfin.Plugin.SmartPlaylist/Pages/smartCollections.js
Normal file
111
Jellyfin.Plugin.SmartPlaylist/Pages/smartCollections.js
Normal file
|
@ -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();
|
||||||
|
});
|
|
@ -35,7 +35,7 @@
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="inputContainer">
|
<div class="inputContainer">
|
||||||
<label class="inputLabel inputLabelUnfocused" for="SmartPlaylistEditEnabled">Enabled</label>
|
<label class="inputLabel inputLabelUnfocused" for="SmartplaylistEditEnabled">Enabled</label>
|
||||||
<div class="fieldDescription">Is the playlist enabled.</div>
|
<div class="fieldDescription">Is the playlist enabled.</div>
|
||||||
<input id="SmartplaylistEditEnabled" type="checkbox" class="emby-input"/>
|
<input id="SmartplaylistEditEnabled" type="checkbox" class="emby-input"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -98,7 +98,7 @@ ApiClient.setSmartPlaylist = function (p) {
|
||||||
}
|
}
|
||||||
|
|
||||||
document.querySelector('#SmartPlaylistConfigPage')
|
document.querySelector('#SmartPlaylistConfigPage')
|
||||||
.addEventListener('pageshow', function() {
|
.addEventListener('viewshow', function() {
|
||||||
Dashboard.showLoadingMsg();
|
Dashboard.showLoadingMsg();
|
||||||
ApiClient.getSmartPlaylists().then(function (playlists) {
|
ApiClient.getSmartPlaylists().then(function (playlists) {
|
||||||
PLAYLISTS = [PLAYLISTS[0]].concat(playlists);
|
PLAYLISTS = [PLAYLISTS[0]].concat(playlists);
|
||||||
|
@ -106,8 +106,6 @@ document.querySelector('#SmartPlaylistConfigPage')
|
||||||
USERS = users;
|
USERS = users;
|
||||||
const selection = document.querySelector('#SmartplaylistSelection');
|
const selection = document.querySelector('#SmartplaylistSelection');
|
||||||
fillPlaylistSelect(PLAYLISTS);
|
fillPlaylistSelect(PLAYLISTS);
|
||||||
console.log('selectedIndex =', selection.selectedIndex);
|
|
||||||
console.log('selectedIndex =', PLAYLISTS[selection.selectedIndex]);
|
|
||||||
fillForm(PLAYLISTS[selection.selectedIndex], USERS);
|
fillForm(PLAYLISTS[selection.selectedIndex], USERS);
|
||||||
Dashboard.hideLoadingMsg();
|
Dashboard.hideLoadingMsg();
|
||||||
});
|
});
|
||||||
|
@ -117,7 +115,6 @@ document.querySelector('#SmartPlaylistConfigPage')
|
||||||
document.querySelector('#SmartplaylistSelection')
|
document.querySelector('#SmartplaylistSelection')
|
||||||
.addEventListener('change', function() {
|
.addEventListener('change', function() {
|
||||||
const selection = document.querySelector('#SmartplaylistSelection');
|
const selection = document.querySelector('#SmartplaylistSelection');
|
||||||
console.log('p =', PLAYLISTS[selection.selectedIndex]);
|
|
||||||
fillForm(PLAYLISTS[selection.selectedIndex], USERS);
|
fillForm(PLAYLISTS[selection.selectedIndex], USERS);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -140,5 +137,4 @@ document.querySelector('#SmartPlaylistConfigForm')
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
})
|
});
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,7 @@ namespace Jellyfin.Plugin.SmartPlaylist {
|
||||||
},
|
},
|
||||||
new PluginPageInfo {
|
new PluginPageInfo {
|
||||||
Name = "Smart Playlists",
|
Name = "Smart Playlists",
|
||||||
|
DisplayName = "Smart Playlists",
|
||||||
EmbeddedResourcePath = string.Format(CultureInfo.InvariantCulture, "{0}.Pages.smartPlaylists.html", GetType().Namespace),
|
EmbeddedResourcePath = string.Format(CultureInfo.InvariantCulture, "{0}.Pages.smartPlaylists.html", GetType().Namespace),
|
||||||
EnableInMainMenu = true,
|
EnableInMainMenu = true,
|
||||||
},
|
},
|
||||||
|
@ -44,8 +45,9 @@ namespace Jellyfin.Plugin.SmartPlaylist {
|
||||||
},
|
},
|
||||||
new PluginPageInfo {
|
new PluginPageInfo {
|
||||||
Name = "Smart Collections",
|
Name = "Smart Collections",
|
||||||
|
DisplayName = "Smart Collections",
|
||||||
EmbeddedResourcePath = string.Format(CultureInfo.InvariantCulture, "{0}.Pages.smartCollections.html", GetType().Namespace),
|
EmbeddedResourcePath = string.Format(CultureInfo.InvariantCulture, "{0}.Pages.smartCollections.html", GetType().Namespace),
|
||||||
//EnableInMainMenu = true,
|
EnableInMainMenu = true,
|
||||||
},
|
},
|
||||||
new PluginPageInfo {
|
new PluginPageInfo {
|
||||||
Name = "smartCollections.js",
|
Name = "smartCollections.js",
|
||||||
|
|
|
@ -101,8 +101,8 @@ namespace Jellyfin.Plugin.SmartPlaylist.ScheduledTasks {
|
||||||
if (_libraryManager.GetItemById(collectionId) is not BoxSet collection) {
|
if (_libraryManager.GetItemById(collectionId) is not BoxSet collection) {
|
||||||
throw new ArgumentException("");
|
throw new ArgumentException("");
|
||||||
}
|
}
|
||||||
var existingItems = collection.Children;
|
var existingItems = collection.LinkedChildren.Select(x => x.ItemId).Where(x => x != null).Select(x => x.Value);
|
||||||
await _collectionManager.RemoveFromCollectionAsync(collectionId, existingItems.Select(x => x.Id));
|
await _collectionManager.RemoveFromCollectionAsync(collectionId, existingItems);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue