✍🏽 Manual search override for AirDC++ query

This commit is contained in:
Rishi Ghan
2022-04-19 14:36:21 -07:00
parent ab19f37007
commit a40b08c990
3 changed files with 765 additions and 585 deletions

View File

@@ -31,85 +31,84 @@ function sleep(ms: number): Promise<NodeJS.Timeout> {
export const search = export const search =
(data: SearchData, ADCPPSocket: any, credentials: any) => (data: SearchData, ADCPPSocket: any, credentials: any) =>
async (dispatch) => { async (dispatch) => {
try { try {
if (!ADCPPSocket.isConnected()) { if (!ADCPPSocket.isConnected()) {
await ADCPPSocket.connect( await ADCPPSocket.connect(
credentials.username, credentials.username,
credentials.password, credentials.password,
true, true,
);
}
const instance: SearchInstance = await ADCPPSocket.post("search");
dispatch({
type: AIRDCPP_SEARCH_IN_PROGRESS,
});
// We want to get notified about every new result in order to make the user experience better
await ADCPPSocket.addListener(
`search`,
"search_result_added",
async (groupedResult) => {
// ...add the received result in the UI
// (it's probably a good idea to have some kind of throttling for the UI updates as there can be thousands of results)
dispatch({
type: AIRDCPP_SEARCH_RESULTS_ADDED,
groupedResult,
});
},
instance.id,
); );
// We also want to update the existing items in our list when new hits arrive for the previously listed files/directories
await ADCPPSocket.addListener(
`search`,
"search_result_updated",
async (groupedResult) => {
// ...update properties of the existing result in the UI
dispatch({
type: AIRDCPP_SEARCH_RESULTS_UPDATED,
groupedResult,
});
},
instance.id,
);
// We need to show something to the user in case the search won't yield any results so that he won't be waiting forever)
// Wait for 5 seconds for any results to arrive after the searches were sent to the hubs
await ADCPPSocket.addListener(
`search`,
"search_hub_searches_sent",
async (searchInfo) => {
await sleep(5000);
// Check the number of received results (in real use cases we should know that even without calling the API)
const currentInstance = await ADCPPSocket.get(
`search/${instance.id}`,
);
if (currentInstance.result_count === 0) {
// ...nothing was received, show an informative message to the user
console.log("No more search results.");
}
// The search can now be considered to be "complete"
// If there's an "in progress" indicator in the UI, that could also be disabled here
dispatch({
type: AIRDCPP_HUB_SEARCHES_SENT,
searchInfo,
instance,
});
},
instance.id,
);
// Finally, perform the actual search
await ADCPPSocket.post(`search/${instance.id}/hub_search`, data);
} catch (error) {
console.log(error);
throw error;
} }
}; const instance: SearchInstance = await ADCPPSocket.post("search");
dispatch({
type: AIRDCPP_SEARCH_IN_PROGRESS,
});
// We want to get notified about every new result in order to make the user experience better
await ADCPPSocket.addListener(
`search`,
"search_result_added",
async (groupedResult) => {
// ...add the received result in the UI
// (it's probably a good idea to have some kind of throttling for the UI updates as there can be thousands of results)
dispatch({
type: AIRDCPP_SEARCH_RESULTS_ADDED,
groupedResult,
});
},
instance.id,
);
// We also want to update the existing items in our list when new hits arrive for the previously listed files/directories
await ADCPPSocket.addListener(
`search`,
"search_result_updated",
async (groupedResult) => {
// ...update properties of the existing result in the UI
dispatch({
type: AIRDCPP_SEARCH_RESULTS_UPDATED,
groupedResult,
});
},
instance.id,
);
// We need to show something to the user in case the search won't yield any results so that he won't be waiting forever)
// Wait for 5 seconds for any results to arrive after the searches were sent to the hubs
await ADCPPSocket.addListener(
`search`,
"search_hub_searches_sent",
async (searchInfo) => {
await sleep(5000);
// Check the number of received results (in real use cases we should know that even without calling the API)
const currentInstance = await ADCPPSocket.get(
`search/${instance.id}`,
);
if (currentInstance.result_count === 0) {
// ...nothing was received, show an informative message to the user
console.log("No more search results.");
}
// The search can now be considered to be "complete"
// If there's an "in progress" indicator in the UI, that could also be disabled here
dispatch({
type: AIRDCPP_HUB_SEARCHES_SENT,
searchInfo,
instance,
});
},
instance.id,
);
// Finally, perform the actual search
await ADCPPSocket.post(`search/${instance.id}/hub_search`, data);
} catch (error) {
console.log(error);
throw error;
}
};
export const downloadAirDCPPItem = export const downloadAirDCPPItem =
( (
@@ -119,122 +118,126 @@ export const downloadAirDCPPItem =
ADCPPSocket: any, ADCPPSocket: any,
credentials: any, credentials: any,
): void => ): void =>
async (dispatch) => { async (dispatch) => {
try { try {
if (!ADCPPSocket.isConnected()) { if (!ADCPPSocket.isConnected()) {
await ADCPPSocket.connect( await ADCPPSocket.connect(
`${credentials.username}`, `${credentials.username}`,
`${credentials.password}`, `${credentials.password}`,
true, true,
);
}
let bundleDBImportResult = {};
const downloadResult = await ADCPPSocket.post(
`search/${instanceId}/results/${resultId}/download`,
); );
let bundleId;
let directoryIds;
if (!isNil(downloadResult.bundle_info)) {
bundleId = downloadResult.bundle_info.id;
}
if (!isNil(downloadResult.directory_download_ids)) {
directoryIds = downloadResult.directory_download_ids.map(
(item) => item.id,
);
}
if (!isNil(downloadResult)) {
bundleDBImportResult = await axios({
method: "POST",
url: `${LIBRARY_SERVICE_BASE_URI}/applyAirDCPPDownloadMetadata`,
headers: {
"Content-Type": "application/json; charset=utf-8",
},
data: {
resultId,
comicObjectId,
searchInstanceId: instanceId,
bundleId,
directoryIds,
},
});
dispatch({
type: AIRDCPP_RESULT_DOWNLOAD_INITIATED,
downloadResult: downloadResult,
bundleDBImportResult,
});
dispatch({
type: IMS_COMIC_BOOK_DB_OBJECT_FETCHED,
comicBookDetail: bundleDBImportResult.data,
IMS_inProgress: false,
});
}
} catch (error) {
throw error;
} }
}; let bundleDBImportResult = {};
const downloadResult = await ADCPPSocket.post(
`search/${instanceId}/results/${resultId}/download`,
);
export const getDownloadProgress = let bundleId;
(comicObjectId: string, ADCPPSocket: any, credentials: any): void => let directoryIds;
async (dispatch) => { if (!isNil(downloadResult.bundle_info)) {
try { bundleId = downloadResult.bundle_info.id;
if (!ADCPPSocket.isConnected()) { }
await ADCPPSocket.connect( if (!isNil(downloadResult.directory_download_ids)) {
`${credentials.username}`, directoryIds = downloadResult.directory_download_ids.map(
`${credentials.password}`, (item) => item.id,
true,
);
}
await ADCPPSocket.addListener(
`queue`,
"queue_bundle_tick",
async (downloadProgressData) => {
dispatch({
type: AIRDCPP_DOWNLOAD_PROGRESS_TICK,
downloadProgressData,
});
},
); );
// File status listener
await ADCPPSocket.addListener(`queue`, "queue_file_status", async (data) => console.log("FILE STATUS", data));
} catch (error) {
throw error;
} }
};
export const getBundlesForComic = if (!isNil(downloadResult)) {
(comicObjectId: string, ADCPPSocket: any, credentials: any) => bundleDBImportResult = await axios({
async (dispatch) => {
try {
if (!ADCPPSocket.isConnected()) {
await ADCPPSocket.connect(
`${credentials.username}`,
`${credentials.password}`,
true,
);
}
const comicObject = await axios({
method: "POST", method: "POST",
url: `${LIBRARY_SERVICE_BASE_URI}/getComicBookById`, url: `${LIBRARY_SERVICE_BASE_URI}/applyAirDCPPDownloadMetadata`,
headers: { headers: {
"Content-Type": "application/json; charset=utf-8", "Content-Type": "application/json; charset=utf-8",
}, },
data: { data: {
id: `${comicObjectId}`, resultId,
comicObjectId,
searchInstanceId: instanceId,
bundleId,
directoryIds,
}, },
}); });
// get only the bundles applicable for the comic
const filteredBundles = comicObject.data.acquisition.directconnect.map(
async ({ bundleId }) => {
return await ADCPPSocket.get(`queue/bundles/${bundleId}`);
},
);
dispatch({ dispatch({
type: AIRDCPP_BUNDLES_FETCHED, type: AIRDCPP_RESULT_DOWNLOAD_INITIATED,
bundles: await Promise.all(filteredBundles), downloadResult: downloadResult,
bundleDBImportResult,
});
dispatch({
type: IMS_COMIC_BOOK_DB_OBJECT_FETCHED,
comicBookDetail: bundleDBImportResult.data,
IMS_inProgress: false,
}); });
} catch (error) {
throw error;
} }
}; } catch (error) {
throw error;
}
};
export const getDownloadProgress =
(comicObjectId: string, ADCPPSocket: any, credentials: any): void =>
async (dispatch) => {
try {
if (!ADCPPSocket.isConnected()) {
await ADCPPSocket.connect(
`${credentials.username}`,
`${credentials.password}`,
true,
);
}
await ADCPPSocket.addListener(
`queue`,
"queue_bundle_tick",
async (downloadProgressData) => {
dispatch({
type: AIRDCPP_DOWNLOAD_PROGRESS_TICK,
downloadProgressData,
});
},
);
// File status listener
await ADCPPSocket.addListener(
`queue`,
"queue_file_status",
async (data) => console.log("FILE STATUS", data),
);
} catch (error) {
throw error;
}
};
export const getBundlesForComic =
(comicObjectId: string, ADCPPSocket: any, credentials: any) =>
async (dispatch) => {
try {
if (!ADCPPSocket.isConnected()) {
await ADCPPSocket.connect(
`${credentials.username}`,
`${credentials.password}`,
true,
);
}
const comicObject = await axios({
method: "POST",
url: `${LIBRARY_SERVICE_BASE_URI}/getComicBookById`,
headers: {
"Content-Type": "application/json; charset=utf-8",
},
data: {
id: `${comicObjectId}`,
},
});
// get only the bundles applicable for the comic
const filteredBundles = comicObject.data.acquisition.directconnect.map(
async ({ bundleId }) => {
return await ADCPPSocket.get(`queue/bundles/${bundleId}`);
},
);
dispatch({
type: AIRDCPP_BUNDLES_FETCHED,
bundles: await Promise.all(filteredBundles),
});
} catch (error) {
throw error;
}
};

View File

@@ -24,7 +24,6 @@ interface IAcquisitionPanelProps {
export const AcquisitionPanel = ( export const AcquisitionPanel = (
props: IAcquisitionPanelProps, props: IAcquisitionPanelProps,
): ReactElement => { ): ReactElement => {
console.log(props);
const issueName = props.query.issue.name; const issueName = props.query.issue.name;
const sanitizedIssueName = issueName.replace(/[^a-zA-Z0-9 ]/g, " "); const sanitizedIssueName = issueName.replace(/[^a-zA-Z0-9 ]/g, " ");
@@ -67,8 +66,19 @@ export const AcquisitionPanel = (
const getDCPPSearchResults = useCallback( const getDCPPSearchResults = useCallback(
async (searchQuery) => { async (searchQuery) => {
const manualQuery = {
query: {
pattern: `${searchQuery.issueName}`,
extensions: ["cbz", "cbr", "cb7"],
},
hub_urls: map(
userSettings.directConnect.client.hubs,
(item) => item.value,
),
priority: 5,
};
dispatch( dispatch(
search(searchQuery, ADCPPSocket, { search(manualQuery, ADCPPSocket, {
username: `${userSettings.directConnect.client.host.username}`, username: `${userSettings.directConnect.client.host.username}`,
password: `${userSettings.directConnect.client.host.password}`, password: `${userSettings.directConnect.client.host.password}`,
}), }),
@@ -113,7 +123,10 @@ export const AcquisitionPanel = (
issueName, issueName,
}} }}
render={({ handleSubmit, form, submitting, pristine, values }) => ( render={({ handleSubmit, form, submitting, pristine, values }) => (
<div className="column is-three-quarters"> <form
onSubmit={handleSubmit}
className="column is-three-quarters"
>
<div className="box search"> <div className="box search">
<div className="columns"> <div className="columns">
<Field name="issueName"> <Field name="issueName">
@@ -135,12 +148,12 @@ export const AcquisitionPanel = (
<div className="column"> <div className="column">
<button <button
type="submit"
className={ className={
isAirDCPPSearchInProgress isAirDCPPSearchInProgress
? "button is-loading is-warning" ? "button is-loading is-warning"
: "button" : "button"
} }
onClick={() => getDCPPSearchResults(dcppQuery)}
> >
<span className="icon is-small"> <span className="icon is-small">
<img src="/img/airdcpp_logo.svg" /> <img src="/img/airdcpp_logo.svg" />
@@ -150,7 +163,7 @@ export const AcquisitionPanel = (
</div> </div>
</div> </div>
</div> </div>
</div> </form>
)} )}
/> />
) : ( ) : (

964
yarn.lock

File diff suppressed because it is too large Load Diff