sonarr-repo-only/frontend/src/Store/Actions/importSeriesActions.js

340 lines
7.8 KiB
JavaScript

import _ from 'lodash';
import { createAction } from 'redux-actions';
import { batchActions } from 'redux-batched-actions';
import { createThunk, handleThunks } from 'Store/thunks';
import createAjaxRequest from 'Utilities/createAjaxRequest';
import getNewSeries from 'Utilities/Series/getNewSeries';
import * as seriesTypes from 'Utilities/Series/seriesTypes';
import getSectionState from 'Utilities/State/getSectionState';
import updateSectionState from 'Utilities/State/updateSectionState';
import { removeItem, set, updateItem } from './baseActions';
import createHandleActions from './Creators/createHandleActions';
import { fetchRootFolders } from './rootFolderActions';
//
// Variables
export const section = 'importSeries';
let concurrentLookups = 0;
let abortCurrentLookup = null;
const queue = [];
//
// State
export const defaultState = {
isLookingUpSeries: false,
isImporting: false,
isImported: false,
importError: null,
items: []
};
//
// Actions Types
export const QUEUE_LOOKUP_SERIES = 'importSeries/queueLookupSeries';
export const START_LOOKUP_SERIES = 'importSeries/startLookupSeries';
export const CANCEL_LOOKUP_SERIES = 'importSeries/cancelLookupSeries';
export const LOOKUP_UNSEARCHED_SERIES = 'importSeries/lookupUnsearchedSeries';
export const CLEAR_IMPORT_SERIES = 'importSeries/clearImportSeries';
export const SET_IMPORT_SERIES_VALUE = 'importSeries/setImportSeriesValue';
export const IMPORT_SERIES = 'importSeries/importSeries';
//
// Action Creators
export const queueLookupSeries = createThunk(QUEUE_LOOKUP_SERIES);
export const startLookupSeries = createThunk(START_LOOKUP_SERIES);
export const importSeries = createThunk(IMPORT_SERIES);
export const lookupUnsearchedSeries = createThunk(LOOKUP_UNSEARCHED_SERIES);
export const clearImportSeries = createAction(CLEAR_IMPORT_SERIES);
export const cancelLookupSeries = createAction(CANCEL_LOOKUP_SERIES);
export const setImportSeriesValue = createAction(SET_IMPORT_SERIES_VALUE, (payload) => {
return {
section,
...payload
};
});
//
// Action Handlers
export const actionHandlers = handleThunks({
[QUEUE_LOOKUP_SERIES]: function(getState, payload, dispatch) {
const {
name,
path,
relativePath,
term,
topOfQueue = false
} = payload;
const state = getState().importSeries;
const item = _.find(state.items, { id: name }) || {
id: name,
term,
path,
relativePath,
isFetching: false,
isPopulated: false,
error: null
};
dispatch(updateItem({
section,
...item,
term,
isQueued: true,
items: []
}));
const itemIndex = queue.indexOf(item.id);
if (itemIndex >= 0) {
queue.splice(itemIndex, 1);
}
if (topOfQueue) {
queue.unshift(item.id);
} else {
queue.push(item.id);
}
if (term && term.length > 2) {
dispatch(startLookupSeries({ start: true }));
}
},
[START_LOOKUP_SERIES]: function(getState, payload, dispatch) {
if (concurrentLookups >= 1) {
return;
}
const state = getState().importSeries;
const {
isLookingUpSeries,
items
} = state;
const queueId = queue[0];
if (payload.start && !isLookingUpSeries) {
dispatch(set({ section, isLookingUpSeries: true }));
} else if (!isLookingUpSeries) {
return;
} else if (!queueId) {
dispatch(set({ section, isLookingUpSeries: false }));
return;
}
concurrentLookups++;
queue.splice(0, 1);
const queued = items.find((i) => i.id === queueId);
dispatch(updateItem({
section,
id: queued.id,
isFetching: true
}));
const { request, abortRequest } = createAjaxRequest({
url: '/series/lookup',
data: {
term: queued.term
}
});
abortCurrentLookup = abortRequest;
request.done((data) => {
const selectedSeries = queued.selectedSeries || data[0];
const itemProps = {
section,
id: queued.id,
isFetching: false,
isPopulated: true,
error: null,
items: data,
isQueued: false,
selectedSeries,
updateOnly: true
};
if (selectedSeries && selectedSeries.seriesType !== seriesTypes.STANDARD) {
itemProps.seriesType = selectedSeries.seriesType;
}
dispatch(updateItem(itemProps));
});
request.fail((xhr) => {
dispatch(updateItem({
section,
id: queued.id,
isFetching: false,
isPopulated: false,
error: xhr,
isQueued: false,
updateOnly: true
}));
});
request.always(() => {
concurrentLookups--;
dispatch(startLookupSeries());
});
},
[LOOKUP_UNSEARCHED_SERIES]: function(getState, payload, dispatch) {
const state = getState().importSeries;
if (state.isLookingUpSeries) {
return;
}
state.items.forEach((item) => {
const id = item.id;
if (
!item.isPopulated &&
!queue.includes(id)
) {
queue.push(item.id);
}
});
if (queue.length) {
dispatch(startLookupSeries({ start: true }));
}
},
[IMPORT_SERIES]: function(getState, payload, dispatch) {
dispatch(set({ section, isImporting: true }));
const ids = payload.ids;
const items = getState().importSeries.items;
const addedIds = [];
const allNewSeries = ids.reduce((acc, id) => {
const item = items.find((i) => i.id === id);
const selectedSeries = item.selectedSeries;
// Make sure we have a selected series and
// the same series hasn't been added yet.
if (selectedSeries && !acc.some((a) => a.tvdbId === selectedSeries.tvdbId)) {
const newSeries = getNewSeries(_.cloneDeep(selectedSeries), item);
newSeries.path = item.path;
addedIds.push(id);
acc.push(newSeries);
}
return acc;
}, []);
const promise = createAjaxRequest({
url: '/series/import',
method: 'POST',
contentType: 'application/json',
data: JSON.stringify(allNewSeries)
}).request;
promise.done((data) => {
dispatch(batchActions([
set({
section,
isImporting: false,
isImported: true,
importError: null
}),
...data.map((series) => updateItem({ section: 'series', ...series })),
...addedIds.map((id) => removeItem({ section, id }))
]));
dispatch(fetchRootFolders());
});
promise.fail((xhr) => {
dispatch(batchActions([
set({
section,
isImporting: false,
isImported: true,
importError: xhr
}),
...addedIds.map((id) => updateItem({
section,
id
}))
]));
});
}
});
//
// Reducers
export const reducers = createHandleActions({
[CANCEL_LOOKUP_SERIES]: function(state) {
queue.splice(0, queue.length);
const items = state.items.map((item) => {
if (item.isQueued) {
return {
...item,
isQueued: false
};
}
return item;
});
return Object.assign({}, state, {
isLookingUpSeries: false,
items
});
},
[CLEAR_IMPORT_SERIES]: function(state) {
if (abortCurrentLookup) {
abortCurrentLookup();
abortCurrentLookup = null;
}
queue.splice(0, queue.length);
return Object.assign({}, state, defaultState);
},
[SET_IMPORT_SERIES_VALUE]: function(state, { payload }) {
const newState = getSectionState(state, section);
const items = newState.items;
const index = items.findIndex((item) => item.id === payload.id);
newState.items = [...items];
if (index >= 0) {
const item = items[index];
newState.items.splice(index, 1, { ...item, ...payload });
} else {
newState.items.push({ ...payload });
}
return updateSectionState(state, section, newState);
}
}, defaultState, section);