New: Project Aphrodite

This commit is contained in:
Qstick
2018-11-23 02:04:42 -05:00
parent 65efa15551
commit 8430cb40ab
1080 changed files with 73015 additions and 0 deletions

View File

@@ -0,0 +1,12 @@
import { createSelector } from 'reselect';
function createAllMoviesSelector() {
return createSelector(
(state) => state.movies,
(movies) => {
return movies.items;
}
);
}
export default createAllMoviesSelector;

View File

@@ -0,0 +1,137 @@
import _ from 'lodash';
import { createSelector } from 'reselect';
import findSelectedFilters from 'Utilities/Filter/findSelectedFilters';
import { filterTypePredicates, filterTypes, sortDirections } from 'Helpers/Props';
function getSortClause(sortKey, sortDirection, sortPredicates) {
if (sortPredicates && sortPredicates.hasOwnProperty(sortKey)) {
return function(item) {
return sortPredicates[sortKey](item, sortDirection);
};
}
return function(item) {
return item[sortKey];
};
}
function filter(items, state) {
const {
selectedFilterKey,
filters,
customFilters,
filterPredicates
} = state;
if (!selectedFilterKey) {
return items;
}
const selectedFilters = findSelectedFilters(selectedFilterKey, filters, customFilters);
return _.filter(items, (item) => {
let i = 0;
let accepted = true;
while (accepted && i < selectedFilters.length) {
const {
key,
value,
type = filterTypes.EQUAL
} = selectedFilters[i];
if (filterPredicates && filterPredicates.hasOwnProperty(key)) {
const predicate = filterPredicates[key];
if (Array.isArray(value)) {
accepted = value.some((v) => predicate(item, v, type));
} else {
accepted = predicate(item, value, type);
}
} else if (item.hasOwnProperty(key)) {
const predicate = filterTypePredicates[type];
if (Array.isArray(value)) {
if (
type === filterTypes.NOT_CONTAINS ||
type === filterTypes.NOT_EQUAL
) {
accepted = value.every((v) => predicate(item[key], v));
} else {
accepted = value.some((v) => predicate(item[key], v));
}
} else {
accepted = predicate(item[key], value);
}
} else {
// Default to false if the filter can't be tested
accepted = false;
}
i++;
}
return accepted;
});
}
function sort(items, state) {
const {
sortKey,
sortDirection,
sortPredicates,
secondarySortKey,
secondarySortDirection
} = state;
const clauses = [];
const orders = [];
clauses.push(getSortClause(sortKey, sortDirection, sortPredicates));
orders.push(sortDirection === sortDirections.ASCENDING ? 'asc' : 'desc');
if (secondarySortKey &&
secondarySortDirection &&
(sortKey !== secondarySortKey ||
sortDirection !== secondarySortDirection)) {
clauses.push(getSortClause(secondarySortKey, secondarySortDirection, sortPredicates));
orders.push(secondarySortDirection === sortDirections.ASCENDING ? 'asc' : 'desc');
}
return _.orderBy(items, clauses, orders);
}
function createCustomFiltersSelector(type, alternateType) {
return createSelector(
(state) => state.customFilters.items,
(customFilters) => {
return customFilters.filter((customFilter) => {
return customFilter.type === type || customFilter.type === alternateType;
});
}
);
}
function createClientSideCollectionSelector(section, uiSection) {
return createSelector(
(state) => _.get(state, section),
(state) => _.get(state, uiSection),
createCustomFiltersSelector(section, uiSection),
(sectionState, uiSectionState = {}, customFilters) => {
const state = Object.assign({}, sectionState, uiSectionState, { customFilters });
const filtered = filter(state.items, state);
const sorted = sort(filtered, state);
return {
...sectionState,
...uiSectionState,
customFilters,
items: sorted,
totalItems: state.items.length
};
}
);
}
export default createClientSideCollectionSelector;

View File

@@ -0,0 +1,14 @@
import { createSelector } from 'reselect';
import { isCommandExecuting } from 'Utilities/Command';
import createCommandSelector from './createCommandSelector';
function createCommandExecutingSelector(name, contraints = {}) {
return createSelector(
createCommandSelector(name, contraints),
(command) => {
return isCommandExecuting(command);
}
);
}
export default createCommandExecutingSelector;

View File

@@ -0,0 +1,14 @@
import { createSelector } from 'reselect';
import { findCommand } from 'Utilities/Command';
import createCommandsSelector from './createCommandsSelector';
function createCommandSelector(name, contraints = {}) {
return createSelector(
createCommandsSelector(),
(commands) => {
return findCommand(commands, { name, ...contraints });
}
);
}
export default createCommandSelector;

View File

@@ -0,0 +1,12 @@
import { createSelector } from 'reselect';
function createCommandsSelector() {
return createSelector(
(state) => state.commands,
(commands) => {
return commands.items;
}
);
}
export default createCommandsSelector;

View File

@@ -0,0 +1,12 @@
import { createSelector } from 'reselect';
function createDimensionsSelector() {
return createSelector(
(state) => state.app.dimensions,
(dimensions) => {
return dimensions;
}
);
}
export default createDimensionsSelector;

View File

@@ -0,0 +1,15 @@
import _ from 'lodash';
import { createSelector } from 'reselect';
import createAllMoviesSelector from './createAllMoviesSelector';
function createExistingMovieSelector() {
return createSelector(
(state, { tmdbId }) => tmdbId,
createAllMoviesSelector(),
(tmdbId, movies) => {
return _.some(movies, { tmdbId });
}
);
}
export default createExistingMovieSelector;

View File

@@ -0,0 +1,26 @@
import _ from 'lodash';
import { createSelector } from 'reselect';
import createAllMoviesSelector from './createAllMoviesSelector';
function createImportMovieItemSelector() {
return createSelector(
(state, { id }) => id,
(state) => state.addMovie,
(state) => state.importMovie,
createAllMoviesSelector(),
(id, addMovie, importMovie, series) => {
const item = _.find(importMovie.items, { id }) || {};
const selectedMovie = item && item.selectedMovie;
const isExistingMovie = !!selectedMovie && _.some(series, { tvdbId: selectedMovie.tvdbId });
return {
defaultMonitor: addMovie.defaults.monitor,
defaultQualityProfileId: addMovie.defaults.qualityProfileId,
...item,
isExistingMovie
};
}
);
}
export default createImportMovieItemSelector;

View File

@@ -0,0 +1,13 @@
import { createSelector } from 'reselect';
import createAllMoviesSelector from './createAllMoviesSelector';
function createMovieCountSelector() {
return createSelector(
createAllMoviesSelector(),
(movies) => {
return movies.length;
}
);
}
export default createMovieCountSelector;

View File

@@ -0,0 +1,17 @@
import { createSelector } from 'reselect';
function createMovieFileSelector() {
return createSelector(
(state, { movieFileId }) => movieFileId,
(state) => state.movieFiles,
(movieFileId, movieFiles) => {
if (!movieFileId) {
return;
}
return movieFiles.items.find((movieFile) => movieFile.id === movieFileId);
}
);
}
export default createMovieFileSelector;

View File

@@ -0,0 +1,15 @@
import _ from 'lodash';
import { createSelector } from 'reselect';
import createAllMoviesSelector from './createAllMoviesSelector';
function createMovieSelector() {
return createSelector(
(state, { movieId }) => movieId,
createAllMoviesSelector(),
(movieId, movies) => {
return _.find(movies, { id: movieId });
}
);
}
export default createMovieSelector;

View File

@@ -0,0 +1,19 @@
import _ from 'lodash';
import { createSelector } from 'reselect';
import createAllMoviesSelector from './createAllMoviesSelector';
function createProfileInUseSelector(profileProp) {
return createSelector(
(state, { id }) => id,
createAllMoviesSelector(),
(id, series) => {
if (!id) {
return false;
}
return _.some(series, { [profileProp]: id });
}
);
}
export default createProfileInUseSelector;

View File

@@ -0,0 +1,63 @@
import _ from 'lodash';
import { createSelector } from 'reselect';
import selectSettings from 'Store/Selectors/selectSettings';
function createProviderSettingsSelector(sectionName) {
return createSelector(
(state, { id }) => id,
(state) => state.settings[sectionName],
(id, section) => {
if (!id) {
const item = _.isArray(section.schema) ? section.selectedSchema : section.schema;
const settings = selectSettings(Object.assign({ name: '' }, item), section.pendingChanges, section.saveError);
const {
isSchemaFetching: isFetching,
isSchemaPopulated: isPopulated,
schemaError: error,
isSaving,
saveError,
isTesting,
pendingChanges
} = section;
return {
isFetching,
isPopulated,
error,
isSaving,
saveError,
isTesting,
pendingChanges,
...settings,
item: settings.settings
};
}
const {
isFetching,
isPopulated,
error,
isSaving,
saveError,
isTesting,
pendingChanges
} = section;
const settings = selectSettings(_.find(section.items, { id }), pendingChanges, saveError);
return {
isFetching,
isPopulated,
error,
isSaving,
saveError,
isTesting,
...settings,
item: settings.settings
};
}
);
}
export default createProviderSettingsSelector;

View File

@@ -0,0 +1,14 @@
import _ from 'lodash';
import { createSelector } from 'reselect';
function createQualityProfileSelector() {
return createSelector(
(state, { qualityProfileId }) => qualityProfileId,
(state) => state.settings.qualityProfiles.items,
(qualityProfileId, qualityProfiles) => {
return _.find(qualityProfiles, { id: qualityProfileId });
}
);
}
export default createQualityProfileSelector;

View File

@@ -0,0 +1,23 @@
import { createSelector } from 'reselect';
function createQueueItemSelector() {
return createSelector(
(state, { episodeId }) => episodeId,
(state) => state.queue.details.items,
(episodeId, details) => {
if (!episodeId) {
return null;
}
return details.find((item) => {
if (item.episode) {
return item.episode.id === episodeId;
}
return false;
});
}
);
}
export default createQueueItemSelector;

View File

@@ -0,0 +1,32 @@
import { createSelector } from 'reselect';
import selectSettings from 'Store/Selectors/selectSettings';
function createSettingsSectionSelector(section) {
return createSelector(
(state) => state.settings[section],
(sectionSettings) => {
const {
isFetching,
isPopulated,
error,
item,
pendingChanges,
isSaving,
saveError
} = sectionSettings;
const settings = selectSettings(item, pendingChanges, saveError);
return {
isFetching,
isPopulated,
error,
isSaving,
saveError,
...settings
};
}
);
}
export default createSettingsSectionSelector;

View File

@@ -0,0 +1,12 @@
import { createSelector } from 'reselect';
function createSystemStatusSelector() {
return createSelector(
(state) => state.system.status,
(status) => {
return status.item;
}
);
}
export default createSystemStatusSelector;

View File

@@ -0,0 +1,13 @@
import { createSelector } from 'reselect';
function createTagDetailsSelector() {
return createSelector(
(state, { id }) => id,
(state) => state.tags.details.items,
(id, tagDetails) => {
return tagDetails.find((t) => t.id === id);
}
);
}
export default createTagDetailsSelector;

View File

@@ -0,0 +1,12 @@
import { createSelector } from 'reselect';
function createTagsSelector() {
return createSelector(
(state) => state.tags.items,
(tags) => {
return tags;
}
);
}
export default createTagsSelector;

View File

@@ -0,0 +1,12 @@
import { createSelector } from 'reselect';
function createUISettingsSelector() {
return createSelector(
(state) => state.settings.ui,
(ui) => {
return ui.item;
}
);
}
export default createUISettingsSelector;

View File

@@ -0,0 +1,104 @@
import _ from 'lodash';
function getValidationFailures(saveError) {
if (!saveError || saveError.status !== 400) {
return [];
}
return _.cloneDeep(saveError.responseJSON);
}
function mapFailure(failure) {
return {
message: failure.errorMessage,
link: failure.infoLink,
detailedMessage: failure.detailedDescription
};
}
function selectSettings(item, pendingChanges, saveError) {
const validationFailures = getValidationFailures(saveError);
// Merge all settings from the item along with pending
// changes to ensure any settings that were not included
// with the item are included.
const allSettings = Object.assign({}, item, pendingChanges);
const settings = _.reduce(allSettings, (result, value, key) => {
if (key === 'fields') {
return result;
}
// Return a flattened value
if (key === 'implementationName') {
result.implementationName = item[key];
return result;
}
const setting = {
value: item[key],
errors: _.map(_.remove(validationFailures, (failure) => {
return failure.propertyName.toLowerCase() === key.toLowerCase() && !failure.isWarning;
}), mapFailure),
warnings: _.map(_.remove(validationFailures, (failure) => {
return failure.propertyName.toLowerCase() === key.toLowerCase() && failure.isWarning;
}), mapFailure)
};
if (pendingChanges.hasOwnProperty(key)) {
setting.previousValue = setting.value;
setting.value = pendingChanges[key];
setting.pending = true;
}
result[key] = setting;
return result;
}, {});
const fields = _.reduce(item.fields, (result, f) => {
const field = Object.assign({ pending: false }, f);
const hasPendingFieldChange = pendingChanges.fields && pendingChanges.fields.hasOwnProperty(field.name);
if (hasPendingFieldChange) {
field.previousValue = field.value;
field.value = pendingChanges.fields[field.name];
field.pending = true;
}
field.errors = _.map(_.remove(validationFailures, (failure) => {
return failure.propertyName.toLowerCase() === field.name.toLowerCase() && !failure.isWarning;
}), mapFailure);
field.warnings = _.map(_.remove(validationFailures, (failure) => {
return failure.propertyName.toLowerCase() === field.name.toLowerCase() && failure.isWarning;
}), mapFailure);
result.push(field);
return result;
}, []);
if (fields.length) {
settings.fields = fields;
}
const validationErrors = _.filter(validationFailures, (failure) => {
return !failure.isWarning;
});
const validationWarnings = _.filter(validationFailures, (failure) => {
return failure.isWarning;
});
return {
settings,
validationErrors,
validationWarnings,
hasPendingChanges: !_.isEmpty(pendingChanges),
hasSettings: !_.isEmpty(settings),
pendingChanges
};
}
export default selectSettings;