From 8b9dd7ec873434008b8b0cd59d0d256e16d009b3 Mon Sep 17 00:00:00 2001 From: Bill Thornton Date: Wed, 10 Apr 2024 03:36:29 -0400 Subject: [PATCH 1/7] Remove dead playlists controller --- src/scripts/playlists.js | 203 --------------------------------------- 1 file changed, 203 deletions(-) delete mode 100644 src/scripts/playlists.js diff --git a/src/scripts/playlists.js b/src/scripts/playlists.js deleted file mode 100644 index 33963bf8a2..0000000000 --- a/src/scripts/playlists.js +++ /dev/null @@ -1,203 +0,0 @@ -import loading from '../components/loading/loading'; -import listView from '../components/listview/listview'; -import cardBuilder from '../components/cardbuilder/cardBuilder'; -import libraryMenu from './libraryMenu'; -import libraryBrowser from './libraryBrowser'; -import imageLoader from '../components/images/imageLoader'; -import * as userSettings from './settings/userSettings'; -import '../elements/emby-itemscontainer/emby-itemscontainer'; -import Dashboard from '../utils/dashboard'; - -export default function (view) { - function getPageData() { - const key = getSavedQueryKey(); - let pageData = data[key]; - - if (!pageData) { - pageData = data[key] = { - query: { - SortBy: 'SortName', - SortOrder: 'Ascending', - IncludeItemTypes: 'Playlist', - Recursive: true, - Fields: 'PrimaryImageAspectRatio,SortName,CumulativeRunTimeTicks,CanDelete', - StartIndex: 0 - }, - view: userSettings.getSavedView(key) || 'Poster' - }; - - if (userSettings.libraryPageSize() > 0) { - pageData.query['Limit'] = userSettings.libraryPageSize(); - } - - pageData.query.ParentId = libraryMenu.getTopParentId(); - userSettings.loadQuerySettings(key, pageData.query); - } - - return pageData; - } - - function getQuery() { - return getPageData().query; - } - - function getSavedQueryKey() { - return `${libraryMenu.getTopParentId()}-playlists`; - } - - function showLoadingMessage() { - loading.show(); - } - - function hideLoadingMessage() { - loading.hide(); - } - - function onViewStyleChange() { - const viewStyle = getPageData().view; - const itemsContainer = view.querySelector('.itemsContainer'); - - if (viewStyle == 'List') { - itemsContainer.classList.add('vertical-list'); - itemsContainer.classList.remove('vertical-wrap'); - } else { - itemsContainer.classList.remove('vertical-list'); - itemsContainer.classList.add('vertical-wrap'); - } - - itemsContainer.innerHTML = ''; - } - - function reloadItems() { - showLoadingMessage(); - const query = getQuery(); - const promise1 = ApiClient.getItems(Dashboard.getCurrentUserId(), query); - // TODO: promise2 is unused, check if necessary. - const promise2 = Dashboard.getCurrentUser(); - Promise.all([promise1, promise2]).then(function (responses) { - const result = responses[0]; - // TODO: Is the scroll necessary? - window.scrollTo(0, 0); - let html = ''; - const viewStyle = getPageData().view; - view.querySelector('.listTopPaging').innerHTML = libraryBrowser.getQueryPagingHtml({ - startIndex: query.StartIndex, - limit: query.Limit, - totalRecordCount: result.TotalRecordCount, - viewButton: false, - showLimit: false, - updatePageSizeSetting: false, - addLayoutButton: true, - layouts: 'List,Poster,PosterCard,Thumb,ThumbCard', - currentLayout: viewStyle - }); - - if (result.TotalRecordCount) { - if (viewStyle == 'List') { - html = listView.getListViewHtml({ - items: result.Items, - sortBy: query.SortBy - }); - } else if (viewStyle == 'PosterCard') { - html = cardBuilder.getCardsHtml({ - items: result.Items, - shape: 'square', - coverImage: true, - showTitle: true, - cardLayout: true - }); - } else if (viewStyle == 'Thumb') { - html = cardBuilder.getCardsHtml({ - items: result.Items, - shape: 'backdrop', - showTitle: true, - centerText: true, - preferThumb: true, - overlayPlayButton: true - }); - } else if (viewStyle == 'ThumbCard') { - html = cardBuilder.getCardsHtml({ - items: result.Items, - shape: 'backdrop', - showTitle: true, - preferThumb: true, - cardLayout: true - }); - } else { - html = cardBuilder.getCardsHtml({ - items: result.Items, - shape: 'square', - showTitle: true, - coverImage: true, - centerText: true, - overlayPlayButton: true - }); - } - view.querySelector('.noItemsMessage').classList.add('hide'); - } else { - view.querySelector('.noItemsMessage').classList.remove('hide'); - } - - const elem = view.querySelector('.itemsContainer'); - elem.innerHTML = html; - imageLoader.lazyChildren(elem); - const btnNextPage = view.querySelector('.btnNextPage'); - - if (btnNextPage) { - btnNextPage.addEventListener('click', function () { - if (userSettings.libraryPageSize() > 0) { - query.StartIndex += query.Limit; - } - reloadItems(); - }); - } - - const btnPreviousPage = view.querySelector('.btnPreviousPage'); - - if (btnPreviousPage) { - btnPreviousPage.addEventListener('click', function () { - if (userSettings.libraryPageSize() > 0) { - query.StartIndex = Math.max(0, query.StartIndex - query.Limit); - } - reloadItems(); - }); - } - - const btnChangeLayout = view.querySelector('.btnChangeLayout'); - - if (btnChangeLayout) { - btnChangeLayout.addEventListener('layoutchange', function (e) { - const layout = e.detail.viewStyle; - getPageData().view = layout; - userSettings.saveViewSetting(getSavedQueryKey(), layout); - onViewStyleChange(); - reloadItems(); - }); - } - - userSettings.saveQuerySettings(getSavedQueryKey(), query); - hideLoadingMessage(); - }); - } - - const data = {}; - view.addEventListener('viewbeforeshow', function () { - reloadItems(); - }); - view.querySelector('.btnNewPlaylist').addEventListener('click', function () { - import('../components/playlisteditor/playlisteditor').then(({ default: PlaylistEditor }) => { - const serverId = ApiClient.serverInfo().Id; - const playlistEditor = new PlaylistEditor(); - playlistEditor.show({ - items: [], - serverId: serverId - }).catch(() => { - // Dialog closed - }); - }).catch(err => { - console.error('[btnNewPlaylist] failed to load playlist editor', err); - }); - }); - onViewStyleChange(); -} - From d3c343be4a75c3c62923cd6c2d97dd2519e4c1fa Mon Sep 17 00:00:00 2001 From: Bill Thornton Date: Fri, 12 Apr 2024 02:03:44 -0400 Subject: [PATCH 2/7] Convert playlist editor to ts --- .../playlisteditor/{playlisteditor.js => playlisteditor.ts} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename src/components/playlisteditor/{playlisteditor.js => playlisteditor.ts} (99%) diff --git a/src/components/playlisteditor/playlisteditor.js b/src/components/playlisteditor/playlisteditor.ts similarity index 99% rename from src/components/playlisteditor/playlisteditor.js rename to src/components/playlisteditor/playlisteditor.ts index 625135a472..a85246a706 100644 --- a/src/components/playlisteditor/playlisteditor.js +++ b/src/components/playlisteditor/playlisteditor.ts @@ -8,7 +8,7 @@ import { pluginManager } from '../pluginManager'; import * as userSettings from '../../scripts/settings/userSettings'; import { appRouter } from '../router/appRouter'; import globalize from '../../scripts/globalize'; -import { PluginType } from '../../types/plugin.ts'; +import { PluginType } from '../../types/plugin'; import '../../elements/emby-button/emby-button'; import '../../elements/emby-input/emby-input'; From fa16daabb019d94dc5fd2e06a835c21c660158f5 Mon Sep 17 00:00:00 2001 From: Bill Thornton Date: Sat, 13 Apr 2024 02:26:41 -0400 Subject: [PATCH 3/7] Fix type errors in playlist editor --- src/apiclient.d.ts | 1 + .../playlisteditor/playlisteditor.ts | 174 +++++++++++------- src/scripts/settings/userSettings.js | 4 +- 3 files changed, 110 insertions(+), 69 deletions(-) diff --git a/src/apiclient.d.ts b/src/apiclient.d.ts index a9f7f61ccd..1bda81641d 100644 --- a/src/apiclient.d.ts +++ b/src/apiclient.d.ts @@ -76,6 +76,7 @@ declare module 'jellyfin-apiclient' { accessToken(): string; addMediaPath(virtualFolderName: string, mediaPath: string, networkSharePath: string, refreshLibrary?: boolean): Promise; addVirtualFolder(name: string, type?: string, refreshLibrary?: boolean, libraryOptions?: any): Promise; + ajax(request: any): Promise; appName(): string; appVersion(): string; authenticateUserByName(name: string, password: string): Promise; diff --git a/src/components/playlisteditor/playlisteditor.ts b/src/components/playlisteditor/playlisteditor.ts index a85246a706..17a5966c14 100644 --- a/src/components/playlisteditor/playlisteditor.ts +++ b/src/components/playlisteditor/playlisteditor.ts @@ -1,53 +1,77 @@ import escapeHtml from 'escape-html'; -import dom from '../../scripts/dom'; +import type { ApiClient } from 'jellyfin-apiclient'; + +import dom from 'scripts/dom'; +import globalize from 'scripts/globalize'; +import * as userSettings from 'scripts/settings/userSettings'; +import { PluginType } from 'types/plugin'; + import dialogHelper from '../dialogHelper/dialogHelper'; import loading from '../loading/loading'; import layoutManager from '../layoutManager'; import { playbackManager } from '../playback/playbackmanager'; import { pluginManager } from '../pluginManager'; -import * as userSettings from '../../scripts/settings/userSettings'; import { appRouter } from '../router/appRouter'; -import globalize from '../../scripts/globalize'; -import { PluginType } from '../../types/plugin'; - -import '../../elements/emby-button/emby-button'; -import '../../elements/emby-input/emby-input'; -import '../../elements/emby-button/paper-icon-button-light'; -import '../../elements/emby-select/emby-select'; -import 'material-design-icons-iconfont'; -import '../formdialog.scss'; import ServerConnections from '../ServerConnections'; -let currentServerId; +import 'elements/emby-button/emby-button'; +import 'elements/emby-input/emby-input'; +import 'elements/emby-button/paper-icon-button-light'; +import 'elements/emby-select/emby-select'; -function onSubmit(e) { - const panel = dom.parentWithClass(this, 'dialog'); +import 'material-design-icons-iconfont'; +import '../formdialog.scss'; - const playlistId = panel.querySelector('#selectPlaylistToAddTo').value; - const apiClient = ServerConnections.getApiClient(currentServerId); +interface DialogElement extends HTMLDivElement { + submitted?: boolean +} - if (playlistId) { - userSettings.set('playlisteditor-lastplaylistid', playlistId); - addToPlaylist(apiClient, panel, playlistId); +interface PlaylistEditorOptions { + items: string[], + serverId: string, + enableAddToPlayQueue?: boolean, + defaultValue?: string +} + +let currentServerId: string; + +function onSubmit(this: HTMLElement, e: Event) { + const panel = dom.parentWithClass(this, 'dialog') as DialogElement | null; + + if (panel) { + const playlistId = panel.querySelector('#selectPlaylistToAddTo')?.value; + const apiClient = ServerConnections.getApiClient(currentServerId); + + if (playlistId) { + userSettings.set('playlisteditor-lastplaylistid', playlistId); + addToPlaylist(apiClient, panel, playlistId) + ?.catch(err => { + console.error('[PlaylistEditor] Failed to add to playlist %s', playlistId, err); + }); + } else { + createPlaylist(apiClient, panel) + ?.catch(err => { + console.error('[PlaylistEditor] Failed to create playlist', err); + }); + } } else { - createPlaylist(apiClient, panel); + console.error('[PlaylistEditor] Dialog element is missing!'); } e.preventDefault(); return false; } -function createPlaylist(apiClient, dlg) { +function createPlaylist(apiClient: ApiClient, dlg: DialogElement) { loading.show(); const url = apiClient.getUrl('Playlists', { - Name: dlg.querySelector('#txtNewPlaylistName').value, - Ids: dlg.querySelector('.fldSelectedItemIds').value || '', + Name: dlg.querySelector('#txtNewPlaylistName')?.value, + Ids: dlg.querySelector('.fldSelectedItemIds')?.value || '', userId: apiClient.getCurrentUserId() - }); - apiClient.ajax({ + return apiClient.ajax({ type: 'POST', url: url, dataType: 'json', @@ -62,12 +86,12 @@ function createPlaylist(apiClient, dlg) { }); } -function redirectToPlaylist(apiClient, id) { +function redirectToPlaylist(apiClient: ApiClient, id: string) { appRouter.showItem(id, apiClient.serverId()); } -function addToPlaylist(apiClient, dlg, id) { - const itemIds = dlg.querySelector('.fldSelectedItemIds').value || ''; +function addToPlaylist(apiClient: ApiClient, dlg: DialogElement, id: string) { + const itemIds = dlg.querySelector('.fldSelectedItemIds')?.value || ''; if (id === 'queue') { playbackManager.queue({ @@ -86,7 +110,7 @@ function addToPlaylist(apiClient, dlg, id) { userId: apiClient.getCurrentUserId() }); - apiClient.ajax({ + return apiClient.ajax({ type: 'POST', url: url @@ -98,16 +122,20 @@ function addToPlaylist(apiClient, dlg, id) { }); } -function triggerChange(select) { +function triggerChange(select: HTMLSelectElement) { select.dispatchEvent(new CustomEvent('change', {})); } -function populatePlaylists(editorOptions, panel) { - const select = panel.querySelector('#selectPlaylistToAddTo'); +function populatePlaylists(editorOptions: PlaylistEditorOptions, panel: DialogElement) { + const select = panel.querySelector('#selectPlaylistToAddTo'); loading.hide(); - panel.querySelector('.newPlaylistInfo').classList.add('hide'); + if (!select) { + return Promise.reject(new Error('Playlist element is missing')); } @@ -173,8 +171,6 @@ function populatePlaylists(editorOptions: PlaylistEditorOptions, panel: DialogEl } triggerChange(select); - - loading.hide(); }); } @@ -236,7 +232,8 @@ function initEditor(content: DialogElement, options: PlaylistEditorOptions, item populatePlaylists(options, content) .catch(err => { console.error('[PlaylistEditor] failed to populate playlists', err); - }); + }) + .finally(loading.hide); } else { content.querySelector('.fldSelectPlaylist')?.classList.add('hide'); From 2ab12bc932b8032e96e4fe73902bb2ffc79d6e00 Mon Sep 17 00:00:00 2001 From: Bill Thornton Date: Sun, 21 Apr 2024 12:08:39 -0400 Subject: [PATCH 7/7] Ensure loading indicator is hidden --- src/components/playlisteditor/playlisteditor.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/components/playlisteditor/playlisteditor.ts b/src/components/playlisteditor/playlisteditor.ts index 940d3fb426..4335fa17ec 100644 --- a/src/components/playlisteditor/playlisteditor.ts +++ b/src/components/playlisteditor/playlisteditor.ts @@ -50,13 +50,13 @@ function onSubmit(this: HTMLElement, e: Event) { if (playlistId) { userSettings.set('playlisteditor-lastplaylistid', playlistId); addToPlaylist(panel, playlistId) - ?.catch(err => { + .catch(err => { console.error('[PlaylistEditor] Failed to add to playlist %s', playlistId, err); }) .finally(loading.hide); } else { createPlaylist(panel) - ?.catch(err => { + .catch(err => { console.error('[PlaylistEditor] Failed to create playlist', err); }) .finally(loading.hide); @@ -105,7 +105,7 @@ function addToPlaylist(dlg: DialogElement, id: string) { }); dlg.submitted = true; dialogHelper.close(dlg); - return; + return Promise.resolve(); } return getPlaylistsApi(api) @@ -131,6 +131,8 @@ function populatePlaylists(editorOptions: PlaylistEditorOptions, panel: DialogEl return Promise.reject(new Error('Playlist