New: Don't require artist mapping

This commit is contained in:
ta264 2020-02-09 19:15:43 +00:00 committed by Qstick
parent 1cc434a498
commit be4e748977
159 changed files with 2934 additions and 4208 deletions

View file

@ -0,0 +1,76 @@
import { createAction } from 'redux-actions';
import { createThunk } from 'Store/thunks';
import createFetchHandler from 'Store/Actions/Creators/createFetchHandler';
import createSetSettingValueReducer from 'Store/Actions/Creators/Reducers/createSetSettingValueReducer';
import createSaveProviderHandler, { createCancelSaveProviderHandler } from 'Store/Actions/Creators/createSaveProviderHandler';
import createRemoveItemHandler from 'Store/Actions/Creators/createRemoveItemHandler';
//
// Variables
export const section = 'settings.rootFolders';
//
// Actions Types
export const FETCH_ROOT_FOLDERS = 'settings/rootFolders/fetchRootFolders';
export const SET_ROOT_FOLDER_VALUE = 'settings/rootFolders/setRootFolderValue';
export const SAVE_ROOT_FOLDER = 'settings/rootFolders/saveRootFolder';
export const CANCEL_SAVE_ROOT_FOLDER = 'settings/rootFolders/cancelSaveRootFolder';
export const DELETE_ROOT_FOLDER = 'settings/rootFolders/deleteRootFolder';
//
// Action Creators
export const fetchRootFolders = createThunk(FETCH_ROOT_FOLDERS);
export const saveRootFolder = createThunk(SAVE_ROOT_FOLDER);
export const cancelSaveRootFolder = createThunk(CANCEL_SAVE_ROOT_FOLDER);
export const deleteRootFolder = createThunk(DELETE_ROOT_FOLDER);
export const setRootFolderValue = createAction(SET_ROOT_FOLDER_VALUE, (payload) => {
return {
section,
...payload
};
});
//
// Details
export default {
//
// State
defaultState: {
isFetching: false,
isPopulated: false,
error: null,
schema: {
defaultTags: []
},
isSaving: false,
saveError: null,
items: [],
pendingChanges: {}
},
//
// Action Handlers
actionHandlers: {
[FETCH_ROOT_FOLDERS]: createFetchHandler(section, '/rootFolder'),
[SAVE_ROOT_FOLDER]: createSaveProviderHandler(section, '/rootFolder'),
[CANCEL_SAVE_ROOT_FOLDER]: createCancelSaveProviderHandler(section),
[DELETE_ROOT_FOLDER]: createRemoveItemHandler(section, '/rootFolder')
},
//
// Reducers
reducers: {
[SET_ROOT_FOLDER_VALUE]: createSetSettingValueReducer(section)
}
};

View file

@ -1,327 +0,0 @@
import _ from 'lodash';
import { createAction } from 'redux-actions';
import { batchActions } from 'redux-batched-actions';
import createAjaxRequest from 'Utilities/createAjaxRequest';
import getSectionState from 'Utilities/State/getSectionState';
import updateSectionState from 'Utilities/State/updateSectionState';
import getNewArtist from 'Utilities/Artist/getNewArtist';
import { createThunk, handleThunks } from 'Store/thunks';
import createHandleActions from './Creators/createHandleActions';
import { set, removeItem, updateItem } from './baseActions';
import { fetchRootFolders } from './rootFolderActions';
//
// Variables
export const section = 'importArtist';
let concurrentLookups = 0;
let abortCurrentLookup = null;
const queue = [];
//
// State
export const defaultState = {
isLookingUpArtist: false,
isImporting: false,
isImported: false,
importError: null,
items: []
};
//
// Actions Types
export const QUEUE_LOOKUP_ARTIST = 'importArtist/queueLookupArtist';
export const START_LOOKUP_ARTIST = 'importArtist/startLookupArtist';
export const CANCEL_LOOKUP_ARTIST = 'importArtist/cancelLookupArtist';
export const LOOKUP_UNSEARCHED_ARTIST = 'importArtist/lookupUnsearchedArtist';
export const CLEAR_IMPORT_ARTIST = 'importArtist/clearImportArtist';
export const SET_IMPORT_ARTIST_VALUE = 'importArtist/setImportArtistValue';
export const IMPORT_ARTIST = 'importArtist/importArtist';
//
// Action Creators
export const queueLookupArtist = createThunk(QUEUE_LOOKUP_ARTIST);
export const startLookupArtist = createThunk(START_LOOKUP_ARTIST);
export const importArtist = createThunk(IMPORT_ARTIST);
export const lookupUnsearchedArtist = createThunk(LOOKUP_UNSEARCHED_ARTIST);
export const clearImportArtist = createAction(CLEAR_IMPORT_ARTIST);
export const cancelLookupArtist = createAction(CANCEL_LOOKUP_ARTIST);
export const setImportArtistValue = createAction(SET_IMPORT_ARTIST_VALUE, (payload) => {
return {
section,
...payload
};
});
//
// Action Handlers
export const actionHandlers = handleThunks({
[QUEUE_LOOKUP_ARTIST]: function(getState, payload, dispatch) {
const {
name,
path,
term,
topOfQueue = false
} = payload;
const state = getState().importArtist;
const item = _.find(state.items, { id: name }) || {
id: name,
term,
path,
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(startLookupArtist({ start: true }));
}
},
[START_LOOKUP_ARTIST]: function(getState, payload, dispatch) {
if (concurrentLookups >= 1) {
return;
}
const state = getState().importArtist;
const {
isLookingUpArtist,
items
} = state;
const queueId = queue[0];
if (payload.start && !isLookingUpArtist) {
dispatch(set({ section, isLookingUpArtist: true }));
} else if (!isLookingUpArtist) {
return;
} else if (!queueId) {
dispatch(set({ section, isLookingUpArtist: 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: '/artist/lookup',
data: {
term: queued.term
}
});
abortCurrentLookup = abortRequest;
request.done((data) => {
dispatch(updateItem({
section,
id: queued.id,
isFetching: false,
isPopulated: true,
error: null,
items: data,
isQueued: false,
selectedArtist: queued.selectedArtist || data[0],
updateOnly: true
}));
});
request.fail((xhr) => {
dispatch(updateItem({
section,
id: queued.id,
isFetching: false,
isPopulated: false,
error: xhr,
isQueued: false,
updateOnly: true
}));
});
request.always(() => {
concurrentLookups--;
dispatch(startLookupArtist());
});
},
[LOOKUP_UNSEARCHED_ARTIST]: function(getState, payload, dispatch) {
const state = getState().importArtist;
if (state.isLookingUpArtist) {
return;
}
state.items.forEach((item) => {
const id = item.id;
if (
!item.isPopulated &&
!queue.includes(id)
) {
queue.push(item.id);
}
});
if (queue.length) {
dispatch(startLookupArtist({ start: true }));
}
},
[IMPORT_ARTIST]: function(getState, payload, dispatch) {
dispatch(set({ section, isImporting: true }));
const ids = payload.ids;
const items = getState().importArtist.items;
const addedIds = [];
const allNewArtist = ids.reduce((acc, id) => {
const item = _.find(items, { id });
const selectedArtist = item.selectedArtist;
// Make sure we have a selected artist and
// the same artist hasn't been added yet.
if (selectedArtist && !_.some(acc, { foreignArtistId: selectedArtist.foreignArtistId })) {
const newArtist = getNewArtist(_.cloneDeep(selectedArtist), item);
newArtist.path = item.path;
addedIds.push(id);
acc.push(newArtist);
}
return acc;
}, []);
const promise = createAjaxRequest({
url: '/artist/import',
method: 'POST',
contentType: 'application/json',
data: JSON.stringify(allNewArtist)
}).request;
promise.done((data) => {
dispatch(batchActions([
set({
section,
isImporting: false,
isImported: true
}),
...data.map((artist) => updateItem({ section: 'artist', ...artist })),
...addedIds.map((id) => removeItem({ section, id }))
]));
dispatch(fetchRootFolders());
});
promise.fail((xhr) => {
dispatch(batchActions(
set({
section,
isImporting: false,
isImported: true
}),
addedIds.map((id) => updateItem({
section,
id,
importError: xhr
}))
));
});
}
});
//
// Reducers
export const reducers = createHandleActions({
[CANCEL_LOOKUP_ARTIST]: 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, {
isLookingUpArtist: false,
items
});
},
[CLEAR_IMPORT_ARTIST]: function(state) {
if (abortCurrentLookup) {
abortCurrentLookup();
abortCurrentLookup = null;
}
queue.splice(0, queue.length);
return Object.assign({}, state, defaultState);
},
[SET_IMPORT_ARTIST_VALUE]: function(state, { payload }) {
const newState = getSectionState(state, section);
const items = newState.items;
const index = _.findIndex(items, { 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);

View file

@ -8,7 +8,6 @@ import * as albums from './albumActions';
import * as trackFiles from './trackFileActions';
import * as albumHistory from './albumHistoryActions';
import * as history from './historyActions';
import * as importArtist from './importArtistActions';
import * as interactiveImportActions from './interactiveImportActions';
import * as oAuth from './oAuthActions';
import * as organizePreview from './organizePreviewActions';
@ -17,7 +16,6 @@ import * as paths from './pathActions';
import * as providerOptions from './providerOptionActions';
import * as queue from './queueActions';
import * as releases from './releaseActions';
import * as rootFolders from './rootFolderActions';
import * as albumStudio from './albumStudioActions';
import * as artist from './artistActions';
import * as artistEditor from './artistEditorActions';
@ -41,7 +39,6 @@ export default [
trackFiles,
albumHistory,
history,
importArtist,
interactiveImportActions,
oAuth,
organizePreview,
@ -50,7 +47,6 @@ export default [
providerOptions,
queue,
releases,
rootFolders,
albumStudio,
artist,
artistEditor,

View file

@ -34,10 +34,10 @@ export const defaultState = {
recentFolders: [],
importMode: 'move',
sortPredicates: {
relativePath: function(item, direction) {
const relativePath = item.relativePath;
path: function(item, direction) {
const path = item.path;
return relativePath.toLowerCase();
return path.toLowerCase();
},
artist: function(item, direction) {

View file

@ -1,97 +0,0 @@
import { batchActions } from 'redux-batched-actions';
import createAjaxRequest from 'Utilities/createAjaxRequest';
import { createThunk, handleThunks } from 'Store/thunks';
import createFetchHandler from './Creators/createFetchHandler';
import createHandleActions from './Creators/createHandleActions';
import createRemoveItemHandler from './Creators/createRemoveItemHandler';
import { set, updateItem } from './baseActions';
//
// Variables
export const section = 'rootFolders';
//
// State
export const defaultState = {
isFetching: false,
isPopulated: false,
error: null,
isSaving: false,
saveError: null,
items: []
};
//
// Actions Types
export const FETCH_ROOT_FOLDERS = 'rootFolders/fetchRootFolders';
export const ADD_ROOT_FOLDER = 'rootFolders/addRootFolder';
export const DELETE_ROOT_FOLDER = 'rootFolders/deleteRootFolder';
//
// Action Creators
export const fetchRootFolders = createThunk(FETCH_ROOT_FOLDERS);
export const addRootFolder = createThunk(ADD_ROOT_FOLDER);
export const deleteRootFolder = createThunk(DELETE_ROOT_FOLDER);
//
// Action Handlers
export const actionHandlers = handleThunks({
[FETCH_ROOT_FOLDERS]: createFetchHandler('rootFolders', '/rootFolder'),
[DELETE_ROOT_FOLDER]: createRemoveItemHandler(
'rootFolders',
'/rootFolder',
(state) => state.rootFolders
),
[ADD_ROOT_FOLDER]: function(getState, payload, dispatch) {
const path = payload.path;
dispatch(set({
section,
isSaving: true
}));
const promise = createAjaxRequest({
url: '/rootFolder',
method: 'POST',
data: JSON.stringify({ path }),
dataType: 'json'
}).request;
promise.done((data) => {
dispatch(batchActions([
updateItem({
section,
...data
}),
set({
section,
isSaving: false,
saveError: null
})
]));
});
promise.fail((xhr) => {
dispatch(set({
section,
isSaving: false,
saveError: xhr
}));
});
}
});
//
// Reducers
export const reducers = createHandleActions({}, defaultState, section);

View file

@ -20,6 +20,7 @@ import qualityDefinitions from './Settings/qualityDefinitions';
import qualityProfiles from './Settings/qualityProfiles';
import releaseProfiles from './Settings/releaseProfiles';
import remotePathMappings from './Settings/remotePathMappings';
import rootFolders from './Settings/rootFolders';
import ui from './Settings/ui';
export * from './Settings/delayProfiles';
@ -41,6 +42,7 @@ export * from './Settings/qualityDefinitions';
export * from './Settings/qualityProfiles';
export * from './Settings/releaseProfiles';
export * from './Settings/remotePathMappings';
export * from './Settings/rootFolders';
export * from './Settings/ui';
//
@ -73,6 +75,7 @@ export const defaultState = {
qualityProfiles: qualityProfiles.defaultState,
releaseProfiles: releaseProfiles.defaultState,
remotePathMappings: remotePathMappings.defaultState,
rootFolders: rootFolders.defaultState,
ui: ui.defaultState
};
@ -113,6 +116,7 @@ export const actionHandlers = handleThunks({
...qualityProfiles.actionHandlers,
...releaseProfiles.actionHandlers,
...remotePathMappings.actionHandlers,
...rootFolders.actionHandlers,
...ui.actionHandlers
});
@ -144,6 +148,7 @@ export const reducers = createHandleActions({
...qualityProfiles.reducers,
...releaseProfiles.reducers,
...remotePathMappings.reducers,
...rootFolders.reducers,
...ui.reducers
}, defaultState, section);

View file

@ -45,11 +45,6 @@ export const defaultState = {
label: 'Path',
isVisible: false
},
{
name: 'relativePath',
label: 'Relative Path',
isVisible: false
},
{
name: 'duration',
label: 'Duration',