|
|
@ -1,5 +1,5 @@ |
|
|
|
import React, { Dispatch, SetStateAction, useEffect, useState } from 'react'; |
|
|
|
import React, { Dispatch, SetStateAction, useEffect, useState } from 'react'; |
|
|
|
import { Box, Button, Checkbox, createStyles, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle, Divider, FormControlLabel, List, ListItem, ListItemIcon, ListItemText, makeStyles, MenuItem, Paper, Select, Theme, Typography } from "@material-ui/core"; |
|
|
|
import { Box, Button, Checkbox, createStyles, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle, Divider, FormControlLabel, LinearProgress, List, ListItem, ListItemIcon, ListItemText, makeStyles, MenuItem, Paper, Select, Theme, Typography } from "@material-ui/core"; |
|
|
|
import StoreLinkIcon from '../../common/StoreLinkIcon'; |
|
|
|
import StoreLinkIcon from '../../common/StoreLinkIcon'; |
|
|
|
import { $enum } from 'ts-enum-util'; |
|
|
|
import { $enum } from 'ts-enum-util'; |
|
|
|
import { IntegrationState, useIntegrations } from '../../../lib/integration/useIntegrations'; |
|
|
|
import { IntegrationState, useIntegrations } from '../../../lib/integration/useIntegrations'; |
|
|
@ -11,6 +11,7 @@ import asyncPool from "tiny-async-pool"; |
|
|
|
import { getSong } from '../../../lib/backend/songs'; |
|
|
|
import { getSong } from '../../../lib/backend/songs'; |
|
|
|
import { getAlbum } from '../../../lib/backend/albums'; |
|
|
|
import { getAlbum } from '../../../lib/backend/albums'; |
|
|
|
import { getArtist } from '../../../lib/backend/artists'; |
|
|
|
import { getArtist } from '../../../lib/backend/artists'; |
|
|
|
|
|
|
|
import { modifyAlbum, modifyArtist, modifySong } from '../../../lib/saveChanges'; |
|
|
|
|
|
|
|
|
|
|
|
const useStyles = makeStyles((theme: Theme) => |
|
|
|
const useStyles = makeStyles((theme: Theme) => |
|
|
|
createStyles({ |
|
|
|
createStyles({ |
|
|
@ -24,6 +25,7 @@ enum BatchJobState { |
|
|
|
Idle = 0, |
|
|
|
Idle = 0, |
|
|
|
Collecting, |
|
|
|
Collecting, |
|
|
|
Running, |
|
|
|
Running, |
|
|
|
|
|
|
|
Finished, |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
interface Task { |
|
|
|
interface Task { |
|
|
@ -99,11 +101,13 @@ async function doLinking( |
|
|
|
console.log("Linking start!", toLink); |
|
|
|
console.log("Linking start!", toLink); |
|
|
|
|
|
|
|
|
|
|
|
// Start the collecting phase.
|
|
|
|
// Start the collecting phase.
|
|
|
|
setStatus({ |
|
|
|
setStatus((s: any) => { |
|
|
|
|
|
|
|
return { |
|
|
|
state: BatchJobState.Collecting, |
|
|
|
state: BatchJobState.Collecting, |
|
|
|
numTasks: 0, |
|
|
|
numTasks: 0, |
|
|
|
tasksSuccess: 0, |
|
|
|
tasksSuccess: 0, |
|
|
|
tasksFailed: 0, |
|
|
|
tasksFailed: 0, |
|
|
|
|
|
|
|
} |
|
|
|
}); |
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
console.log("Starting collection"); |
|
|
|
console.log("Starting collection"); |
|
|
@ -127,17 +131,30 @@ async function doLinking( |
|
|
|
console.log("Done collecting.", tasks) |
|
|
|
console.log("Done collecting.", tasks) |
|
|
|
// Start the linking phase.
|
|
|
|
// Start the linking phase.
|
|
|
|
setStatus((status: BatchJobStatus) => { |
|
|
|
setStatus((status: BatchJobStatus) => { |
|
|
|
status.state = BatchJobState.Running; |
|
|
|
return { |
|
|
|
status.numTasks = tasks.length; |
|
|
|
...status, |
|
|
|
console.log("Collected status:", status) |
|
|
|
state: BatchJobState.Running, |
|
|
|
return status; |
|
|
|
numTasks: tasks.length |
|
|
|
|
|
|
|
} |
|
|
|
}); |
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
let makeJob: (t: Task) => Promise<void> = (t: Task) => { |
|
|
|
let makeJob: (t: Task) => Promise<void> = (t: Task) => { |
|
|
|
let integration = integrations.find((i: IntegrationState) => i.id === t.integrationId); |
|
|
|
let integration = integrations.find((i: IntegrationState) => i.id === t.integrationId); |
|
|
|
return (async () => { |
|
|
|
return (async () => { |
|
|
|
let onSuccess = () => setStatus((s: BatchJobStatus) => { s.tasksSuccess += 1; return s; }); |
|
|
|
let onSuccess = () => |
|
|
|
let onFail = () => setStatus((s: BatchJobStatus) => { s.tasksFailed += 1; return s; }); |
|
|
|
setStatus((s: BatchJobStatus) => { |
|
|
|
|
|
|
|
return { |
|
|
|
|
|
|
|
...s, |
|
|
|
|
|
|
|
tasksSuccess: s.tasksSuccess + 1, |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
let onFail = () => |
|
|
|
|
|
|
|
setStatus((s: BatchJobStatus) => { |
|
|
|
|
|
|
|
return { |
|
|
|
|
|
|
|
...s, |
|
|
|
|
|
|
|
tasksFailed: s.tasksFailed + 1, |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}); |
|
|
|
try { |
|
|
|
try { |
|
|
|
if (integration === undefined) { return; } |
|
|
|
if (integration === undefined) { return; } |
|
|
|
console.log('integration search:', integration) |
|
|
|
console.log('integration search:', integration) |
|
|
@ -154,18 +171,38 @@ async function doLinking( |
|
|
|
[ItemType.Artist]: getArtist, |
|
|
|
[ItemType.Artist]: getArtist, |
|
|
|
} |
|
|
|
} |
|
|
|
let queryFuncs: any = { |
|
|
|
let queryFuncs: any = { |
|
|
|
[ItemType.Song]: (s: any) => `${s.title}`, |
|
|
|
[ItemType.Song]: (s: any) => `${s.title}` + |
|
|
|
[ItemType.Album]: (s: any) => `${s.name}`, |
|
|
|
`${s.artists && s.artists.length > 0 && ` ${s.artists[0].name}`}` + |
|
|
|
|
|
|
|
`${s.albums && s.albums.length > 0 && ` ${s.albums[0].name}`}`, |
|
|
|
|
|
|
|
[ItemType.Album]: (s: any) => `${s.name}` + |
|
|
|
|
|
|
|
`${s.artists && s.artists.length > 0 && ` ${s.artists[0].name}`}`, |
|
|
|
[ItemType.Artist]: (s: any) => `${s.name}`, |
|
|
|
[ItemType.Artist]: (s: any) => `${s.name}`, |
|
|
|
} |
|
|
|
} |
|
|
|
let query = queryFuncs[t.itemType](await getFuncs[t.itemType](t.itemId)); |
|
|
|
let modifyFuncs: any = { |
|
|
|
|
|
|
|
[ItemType.Song]: modifySong, |
|
|
|
|
|
|
|
[ItemType.Album]: modifyAlbum, |
|
|
|
|
|
|
|
[ItemType.Artist]: modifyArtist, |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
let item = await getFuncs[t.itemType](t.itemId); |
|
|
|
|
|
|
|
let query = queryFuncs[t.itemType](item); |
|
|
|
let candidates = await searchFuncs[t.itemType]( |
|
|
|
let candidates = await searchFuncs[t.itemType]( |
|
|
|
query, |
|
|
|
query, |
|
|
|
1, |
|
|
|
1, |
|
|
|
); |
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var success = false; |
|
|
|
|
|
|
|
if (candidates && candidates.length && candidates.length > 0 && candidates[0].url) { |
|
|
|
|
|
|
|
await modifyFuncs[t.itemType]( |
|
|
|
|
|
|
|
t.itemId, |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
storeLinks: [...item.storeLinks, candidates[0].url] |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
) |
|
|
|
|
|
|
|
success = true; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
console.log(query, candidates); |
|
|
|
console.log(query, candidates); |
|
|
|
if (candidates && candidates.length && candidates.length > 0) { |
|
|
|
if (success) { |
|
|
|
onSuccess(); |
|
|
|
onSuccess(); |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
onFail(); |
|
|
|
onFail(); |
|
|
@ -178,13 +215,14 @@ async function doLinking( |
|
|
|
})(); |
|
|
|
})(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
await asyncPool(4, tasks, makeJob); |
|
|
|
await asyncPool(8, tasks, makeJob); |
|
|
|
|
|
|
|
|
|
|
|
// Finalize.
|
|
|
|
// Finalize.
|
|
|
|
setStatus((status: BatchJobStatus) => { |
|
|
|
setStatus((status: BatchJobStatus) => { |
|
|
|
status.state = BatchJobState.Idle; |
|
|
|
return { |
|
|
|
console.log("Done running:", status) |
|
|
|
...status, |
|
|
|
return status; |
|
|
|
state: BatchJobState.Finished, |
|
|
|
|
|
|
|
} |
|
|
|
}); |
|
|
|
}); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -193,16 +231,31 @@ function ProgressDialog(props: { |
|
|
|
onClose: () => void, |
|
|
|
onClose: () => void, |
|
|
|
status: BatchJobStatus, |
|
|
|
status: BatchJobStatus, |
|
|
|
}) { |
|
|
|
}) { |
|
|
|
|
|
|
|
let donePercent = ((props.status.tasksFailed + props.status.tasksSuccess) / (props.status.numTasks || 1)) * 100; |
|
|
|
return <Dialog |
|
|
|
return <Dialog |
|
|
|
open={props.open} |
|
|
|
open={props.open} |
|
|
|
onClose={props.onClose} |
|
|
|
onClose={props.onClose} |
|
|
|
> |
|
|
|
> |
|
|
|
<DialogTitle>Batch linking in progress...</DialogTitle> |
|
|
|
{props.status.state === BatchJobState.Collecting || props.status.state === BatchJobState.Running && |
|
|
|
|
|
|
|
<DialogTitle>Batch linking in progress...</DialogTitle>} |
|
|
|
|
|
|
|
{props.status.state === BatchJobState.Finished && |
|
|
|
|
|
|
|
<DialogTitle>Batch linking finished</DialogTitle>} |
|
|
|
<DialogContent> |
|
|
|
<DialogContent> |
|
|
|
|
|
|
|
{props.status.state === BatchJobState.Collecting || props.status.state === BatchJobState.Running && |
|
|
|
<DialogContentText> |
|
|
|
<DialogContentText> |
|
|
|
Closing or refreshing this page will interrupt and abort the process. |
|
|
|
Closing or refreshing this page will interrupt and abort the process. |
|
|
|
</DialogContentText> |
|
|
|
</DialogContentText>} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<Box minWidth="200px"><LinearProgress variant="determinate" color="secondary" value={donePercent} /></Box> |
|
|
|
|
|
|
|
<Typography> |
|
|
|
|
|
|
|
Found: {props.status.tasksSuccess}<br /> |
|
|
|
|
|
|
|
Failed: {props.status.tasksFailed}<br /> |
|
|
|
|
|
|
|
Total: {props.status.numTasks}<br /> |
|
|
|
|
|
|
|
</Typography> |
|
|
|
</DialogContent> |
|
|
|
</DialogContent> |
|
|
|
|
|
|
|
{props.status.state === BatchJobState.Finished && <DialogActions> |
|
|
|
|
|
|
|
<Button variant="contained" onClick={props.onClose}>Done</Button> |
|
|
|
|
|
|
|
</DialogActions>} |
|
|
|
</Dialog> |
|
|
|
</Dialog> |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -380,8 +433,15 @@ export default function BatchLinkDialog(props: { |
|
|
|
}} |
|
|
|
}} |
|
|
|
/> |
|
|
|
/> |
|
|
|
<ProgressDialog |
|
|
|
<ProgressDialog |
|
|
|
open={jobStatus.state === BatchJobState.Collecting || jobStatus.state === BatchJobState.Running} |
|
|
|
open={jobStatus.state === BatchJobState.Collecting || jobStatus.state === BatchJobState.Running || jobStatus.state === BatchJobState.Finished} |
|
|
|
onClose={() => { }} |
|
|
|
onClose={() => { |
|
|
|
|
|
|
|
setJobStatus({ |
|
|
|
|
|
|
|
numTasks: 0, |
|
|
|
|
|
|
|
tasksSuccess: 0, |
|
|
|
|
|
|
|
tasksFailed: 0, |
|
|
|
|
|
|
|
state: BatchJobState.Idle, |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
}} |
|
|
|
status={jobStatus} |
|
|
|
status={jobStatus} |
|
|
|
/> |
|
|
|
/> |
|
|
|
</> |
|
|
|
</> |
|
|
|