285 lines
7.5 KiB
JavaScript
Executable File
285 lines
7.5 KiB
JavaScript
Executable File
import { createSelector } from 'reselect';
|
|
import { indexToArray } from './arrays';
|
|
import { isLoading } from './helpers';
|
|
import { i18n } from '../locale';
|
|
|
|
const getItem = (state, uri) => state.core.items[uri];
|
|
const getItems = (state) => state.core.items;
|
|
const getLoadQueue = (state) => state.ui.load_queue;
|
|
const getProcesses = (state) => state.ui.processes;
|
|
const getLibrary = (state, uri) => state.core.libraries[uri];
|
|
const getLibraries = (state) => state.core.libraries;
|
|
const getSearchResults = (state) => state.core.search_results;
|
|
const getGridGlowEnabled = (state) => state.ui.grid_glow_enabled;
|
|
const getSorts = (state) => state.ui.sort;
|
|
const getMopidySettings = (state) => state.mopidy;
|
|
|
|
const makeItemSelector = (uri) => createSelector(
|
|
[getItems],
|
|
(items) => {
|
|
if (Array.isArray(uri)) {
|
|
return indexToArray(items, uri);
|
|
}
|
|
return items[uri];
|
|
},
|
|
);
|
|
const makeArtistSelector = (uri) => createSelector(
|
|
[getItems],
|
|
(items) => {
|
|
const artist = items[uri];
|
|
const albums = artist?.albums_uris ? indexToArray(items, artist.albums_uris) : [];
|
|
return {
|
|
...artist,
|
|
albums,
|
|
};
|
|
},
|
|
);
|
|
const makeLoadingSelector = (keys) => createSelector(
|
|
[getLoadQueue],
|
|
(loadQueue) => isLoading(loadQueue, keys),
|
|
);
|
|
|
|
const getQueueHistory = (state) => state.mopidy.queue_history;
|
|
const queueHistorySelector = createSelector(
|
|
[getItems, getQueueHistory],
|
|
(items, queueHistory) => {
|
|
if (!queueHistory || !queueHistory.length) return [];
|
|
|
|
return queueHistory.map((item) => ({
|
|
...item,
|
|
...(items[item.uri] || {}),
|
|
}));
|
|
},
|
|
);
|
|
|
|
const getLibrarySource = (state, name) => state.ui[`library_${name}_source`] || 'all';
|
|
const makeLibrarySelector = (name, filtered = true) => createSelector(
|
|
[getLibraries, getItems, getLibrarySource],
|
|
(libraries, items, source) => {
|
|
const selectedLibraries = !filtered || source === 'all'
|
|
? indexToArray(libraries).filter((l) => l.type === name)
|
|
: indexToArray(libraries, [source]);
|
|
|
|
const itemUris = selectedLibraries.reduce(
|
|
(acc, library) => [...acc, ...library.items_uris],
|
|
[],
|
|
);
|
|
return indexToArray(items, itemUris);
|
|
},
|
|
);
|
|
|
|
const makeSearchResultsSelector = (term, type) => createSelector(
|
|
[getSearchResults],
|
|
(searchResults) => {
|
|
if (!searchResults || searchResults.query.term !== term) return [];
|
|
return searchResults[type] || [];
|
|
},
|
|
);
|
|
|
|
const makeProcessProgressSelector = (keys) => createSelector(
|
|
[getProcesses],
|
|
(processes) => {
|
|
const selectedProcesses = keys
|
|
.map((key) => processes[key] || {})
|
|
.filter((i) => i.status === 'running');
|
|
|
|
if (!selectedProcesses.length) return undefined;
|
|
|
|
let total = 0;
|
|
let remaining = 0;
|
|
selectedProcesses.forEach((process) => {
|
|
if (process.total) total += process.total;
|
|
if (process.remaining) remaining += process.remaining;
|
|
});
|
|
return {
|
|
total,
|
|
remaining,
|
|
percent: total && remaining ? ((total - remaining) / total).toFixed(4) : 0,
|
|
};
|
|
},
|
|
);
|
|
|
|
const providers = {
|
|
playlists: [
|
|
{
|
|
scheme: 'm3u:',
|
|
uri: 'm3u:playlists',
|
|
title: i18n('services.mopidy.local'),
|
|
},
|
|
{
|
|
scheme: 'spotify:',
|
|
uri: 'spotify:library:playlists',
|
|
title: i18n('services.spotify.title'),
|
|
},
|
|
{
|
|
scheme: 'tidal:',
|
|
uri: 'tidal:my_playlists',
|
|
title: i18n('services.tidal.title'),
|
|
},
|
|
{
|
|
scheme: 'jellyfin:',
|
|
uri: 'jellyfin:playlists',
|
|
title: i18n('services.jellyfin.title'),
|
|
},
|
|
],
|
|
albums: [
|
|
{
|
|
scheme: 'local:',
|
|
setting_name: 'library_albums_uri',
|
|
title: i18n('services.mopidy.local'),
|
|
},
|
|
{
|
|
scheme: 'gmusic:',
|
|
uri: 'gmusic:album',
|
|
title: i18n('services.google.title'),
|
|
},
|
|
{
|
|
scheme: 'spotify:',
|
|
uri: 'spotify:library:albums',
|
|
title: i18n('services.spotify.title'),
|
|
},
|
|
{
|
|
scheme: 'tidal:',
|
|
uri: 'tidal:my_albums',
|
|
title: i18n('services.tidal.title'),
|
|
},
|
|
{
|
|
scheme: 'ytmusic:',
|
|
uri: 'ytmusic:album',
|
|
title: i18n('services.youtube.title'),
|
|
},
|
|
{
|
|
scheme: 'jellyfin:',
|
|
uri: 'jellyfin:albums',
|
|
title: i18n('services.jellyfin.title'),
|
|
},
|
|
],
|
|
artists: [
|
|
{
|
|
scheme: 'local:',
|
|
setting_name: 'library_artists_uri',
|
|
title: i18n('services.mopidy.local'),
|
|
},
|
|
{
|
|
scheme: 'gmusic:',
|
|
uri: 'gmusic:artist',
|
|
title: i18n('services.google.title'),
|
|
},
|
|
{
|
|
scheme: 'spotify:',
|
|
uri: 'spotify:library:artists',
|
|
title: i18n('services.spotify.title'),
|
|
},
|
|
{
|
|
scheme: 'tidal:',
|
|
uri: 'tidal:my_artists',
|
|
title: i18n('services.tidal.title'),
|
|
},
|
|
{
|
|
scheme: 'ytmusic:',
|
|
uri: 'ytmusic:artist',
|
|
title: i18n('services.youtube.title'),
|
|
},
|
|
{
|
|
scheme: 'jellyfin:',
|
|
uri: 'jellyfin:artists',
|
|
title: i18n('services.jellyfin.title'),
|
|
},
|
|
],
|
|
tracks: [
|
|
{
|
|
scheme: 'local:',
|
|
setting_name: 'library_tracks_uri',
|
|
title: i18n('services.mopidy.local'),
|
|
},
|
|
{
|
|
scheme: 'spotify:',
|
|
uri: 'spotify:library:tracks',
|
|
title: i18n('services.spotify.title'),
|
|
},
|
|
],
|
|
moods: [
|
|
{
|
|
scheme: 'spotify:',
|
|
uri: 'spotify:categories',
|
|
title: i18n('services.spotify.title'),
|
|
},
|
|
{
|
|
scheme: 'tidal:',
|
|
uri: 'tidal:moods',
|
|
title: i18n('services.tidal.title'),
|
|
},
|
|
{
|
|
scheme: 'tidal:',
|
|
uri: 'tidal:genres',
|
|
title: i18n('services.tidal.title'),
|
|
},
|
|
{
|
|
scheme: 'ytmusic:',
|
|
uri: 'ytmusic:mood',
|
|
title: i18n('services.youtube.title'),
|
|
},
|
|
],
|
|
featured_playlists: [
|
|
{
|
|
scheme: 'spotify:',
|
|
uri: 'spotify:featured',
|
|
title: i18n('services.spotify.title'),
|
|
},
|
|
{
|
|
scheme: 'ytmusic:',
|
|
uri: 'ytmusic:auto',
|
|
title: i18n('services.youtube.title'),
|
|
},
|
|
],
|
|
};
|
|
const getProvider = (type, scheme) => providers[type]?.find((p) => p.scheme === scheme);
|
|
const getUriSchemes = (state) => state.mopidy.uri_schemes || [];
|
|
const applyUriSettingToProviders = (filteredProviders, mopidySettings) => filteredProviders.map(
|
|
({ setting_name, ...rest }) => ({
|
|
uri: setting_name ? mopidySettings[setting_name] : undefined,
|
|
...rest,
|
|
}),
|
|
);
|
|
const makeProvidersSelector = (context) => createSelector(
|
|
[getUriSchemes, getMopidySettings],
|
|
(schemes, mopidySettings) => {
|
|
if (!providers[context]) return [];
|
|
const results = providers[context].filter((p) => schemes.indexOf(p.scheme) > -1);
|
|
return applyUriSettingToProviders(results, mopidySettings);
|
|
},
|
|
);
|
|
const makeSortSelector = (key, defaultField = 'sort_id') => createSelector(
|
|
[getSorts],
|
|
(sorts) => [
|
|
sorts[key]?.field || defaultField,
|
|
sorts[key]?.reverse || false,
|
|
],
|
|
);
|
|
|
|
const getSortSelector = (state, key, defaultField = 'sort_id') => {
|
|
const result = state.ui.sort[key];
|
|
return [
|
|
result?.field || defaultField,
|
|
result?.reverse || false,
|
|
];
|
|
};
|
|
|
|
export {
|
|
getItem,
|
|
getLibrary,
|
|
getGridGlowEnabled,
|
|
getLibrarySource,
|
|
makeItemSelector,
|
|
makeArtistSelector,
|
|
makeLibrarySelector,
|
|
makeLoadingSelector,
|
|
makeSearchResultsSelector,
|
|
makeProcessProgressSelector,
|
|
queueHistorySelector,
|
|
makeProvidersSelector,
|
|
getProvider,
|
|
makeSortSelector,
|
|
getSortSelector,
|
|
};
|