diff --git a/src/client/actions/comicinfo.actions.tsx b/src/client/actions/comicinfo.actions.tsx index 68b3b6f..5574f15 100644 --- a/src/client/actions/comicinfo.actions.tsx +++ b/src/client/actions/comicinfo.actions.tsx @@ -1,6 +1,6 @@ import axios from "axios"; import rateLimiter from "axios-rate-limit"; -import { map } from "lodash"; + import qs from "qs"; import { IExtractionOptions } from "threetwo-ui-typings"; import { @@ -9,8 +9,6 @@ import { CV_API_GENERIC_FAILURE, IMS_COMIC_BOOK_DB_OBJECT_CALL_IN_PROGRESS, IMS_COMIC_BOOK_DB_OBJECT_FETCHED, - IMS_COMIC_BOOK_ARCHIVE_EXTRACTION_SUCCESS, - IMS_COMIC_BOOK_ARCHIVE_EXTRACTION_CALL_IN_PROGRESS, CV_ISSUES_METADATA_CALL_IN_PROGRESS, CV_CLEANUP, IMS_COMIC_BOOKS_DB_OBJECTS_FETCHED, @@ -175,39 +173,3 @@ export const applyComicVineMatch = IMS_inProgress: false, }); }; - -export const extractComicArchive = - (path: string, options: IExtractionOptions) => async (dispatch) => { - const comicBookPages: string[] = []; - dispatch({ - type: IMS_COMIC_BOOK_ARCHIVE_EXTRACTION_CALL_IN_PROGRESS, - }); - const extractedComicBookArchive = await axios({ - method: "POST", - url: `${LIBRARY_SERVICE_BASE_URI}/unrarArchive`, - headers: { - "Content-Type": "application/json; charset=utf-8", - }, - data: { - options, - filePath: path, - }, - }); - map(extractedComicBookArchive.data, (page) => { - const foo = page.path.split("/"); - const folderName = foo[foo.length - 1]; - const imagePath = encodeURI( - `${LIBRARY_SERVICE_BASE_URI}/userdata/expanded/` + - folderName + - `/` + - page.name + - page.extension, - ); - comicBookPages.push(imagePath); - }); - console.log(comicBookPages); - dispatch({ - type: IMS_COMIC_BOOK_ARCHIVE_EXTRACTION_SUCCESS, - extractedComicBookArchive: comicBookPages, - }); - }; diff --git a/src/client/actions/fileops.actions.tsx b/src/client/actions/fileops.actions.tsx index f263866..a02c085 100644 --- a/src/client/actions/fileops.actions.tsx +++ b/src/client/actions/fileops.actions.tsx @@ -2,7 +2,9 @@ import axios from "axios"; import { IFolderData } from "threetwo-ui-typings"; import { COMICVINE_SERVICE_URI, + IMAGETRANSFORMATION_SERVICE_BASE_URI, LIBRARY_SERVICE_BASE_URI, + LIBRARY_SERVICE_HOST, } from "../constants/endpoints"; import { IMS_COMIC_BOOK_GROUPS_FETCHED, @@ -16,9 +18,13 @@ import { IMS_CV_METADATA_IMPORT_SUCCESSFUL, IMS_CV_METADATA_IMPORT_FAILED, LS_IMPORT, + IMG_ANALYSIS_CALL_IN_PROGRESS, + IMG_ANALYSIS_DATA_FETCH_SUCCESS, + IMS_COMIC_BOOK_ARCHIVE_EXTRACTION_SUCCESS, + IMS_COMIC_BOOK_ARCHIVE_EXTRACTION_CALL_IN_PROGRESS, } from "../constants/action-types"; import { success } from "react-notification-system-redux"; -import { isNil } from "lodash"; +import { isNil, map } from "lodash"; export async function walkFolder(path: string): Promise> { return axios @@ -212,3 +218,61 @@ export const fetchComicVineMatches = type: CV_CLEANUP, }); }; + +export const extractComicArchive = + (path: string, options: IExtractionOptions) => async (dispatch) => { + const comicBookPages: string[] = []; + dispatch({ + type: IMS_COMIC_BOOK_ARCHIVE_EXTRACTION_CALL_IN_PROGRESS, + }); + const extractedComicBookArchive = await axios({ + method: "POST", + url: `${LIBRARY_SERVICE_BASE_URI}/unrarArchive`, + headers: { + "Content-Type": "application/json; charset=utf-8", + }, + data: { + options, + filePath: path, + }, + }); + map(extractedComicBookArchive.data, (page) => { + const pathItems = page.filePath.split("/"); + const folderName = pathItems[pathItems.length - 2]; + + const imagePath = encodeURI( + `${LIBRARY_SERVICE_HOST}/userdata/expanded/` + + folderName + + `/` + + page.name + + page.extension, + ); + comicBookPages.push(imagePath); + }); + console.log(comicBookPages); + dispatch({ + type: IMS_COMIC_BOOK_ARCHIVE_EXTRACTION_SUCCESS, + extractedComicBookArchive: comicBookPages, + }); + }; + +export const analyzeImage = + (imageFilePath: string | Buffer) => async (dispatch) => { + console.log(imageFilePath); + dispatch({ + type: IMG_ANALYSIS_CALL_IN_PROGRESS, + }); + + const foo = await axios({ + url: `${IMAGETRANSFORMATION_SERVICE_BASE_URI}/analyze`, + method: "POST", + data: { + imageFilePath, + }, + }); + console.log(foo); + dispatch({ + type: IMG_ANALYSIS_DATA_FETCH_SUCCESS, + result: foo.data, + }); + }; diff --git a/src/client/assets/scss/App.scss b/src/client/assets/scss/App.scss index e409805..f9962fe 100644 --- a/src/client/assets/scss/App.scss +++ b/src/client/assets/scss/App.scss @@ -14,6 +14,13 @@ $size-8: 0.9rem; font-size: $size-8; } + +// global style overrides + +pre { + border-radius: 0.5rem; +} + .container { margin-top: 2em; } @@ -307,6 +314,15 @@ $size-8: 0.9rem; } // Comic Detail +.control-palette { + display: inline-block; + i { + display: flex; + justify-content: center; + align-items: center; + // padding: 1.5rem 2rem; + } +} // airdcpp downloads tab .tabs { diff --git a/src/client/components/ComicDetail/ActionMenu/Menu.tsx b/src/client/components/ComicDetail/ActionMenu/Menu.tsx index ecba5ef..a5fdabe 100644 --- a/src/client/components/ComicDetail/ActionMenu/Menu.tsx +++ b/src/client/components/ComicDetail/ActionMenu/Menu.tsx @@ -14,16 +14,13 @@ export const Menu = (props): ReactElement => { let issueSearchQuery: IComicVineSearchQuery = {} as IComicVineSearchQuery; if (!isUndefined(data.rawFileDetails)) { + console.log(data.rawFileDetails); issueSearchQuery = refineQuery(data.rawFileDetails.name); - if (data.rawFileDetails.containedIn !== "comics") { - seriesSearchQuery = refineQuery( - data.rawFileDetails.containedIn.split("/").pop(), - ); - } } else if (!isEmpty(data.sourcedMetadata)) { issueSearchQuery = refineQuery(data.sourcedMetadata.comicvine.name); } + console.log(seriesSearchQuery); dispatch(fetchComicVineMatches(data, issueSearchQuery, seriesSearchQuery)); setSlidingPanelContentId("CVMatches"); setVisible(true); diff --git a/src/client/components/ComicDetail/Tabs/ArchiveOperations.tsx b/src/client/components/ComicDetail/Tabs/ArchiveOperations.tsx index 3ad3518..63075f5 100644 --- a/src/client/components/ComicDetail/Tabs/ArchiveOperations.tsx +++ b/src/client/components/ComicDetail/Tabs/ArchiveOperations.tsx @@ -1,9 +1,11 @@ -import React, { ReactElement, useCallback } from "react"; +import React, { ReactElement, useCallback, useState } from "react"; import { useSelector, useDispatch } from "react-redux"; import { DnD } from "../../DnD"; import { isEmpty } from "lodash"; import Sticky from "react-stickynode"; -import { extractComicArchive } from "../../../actions/comicinfo.actions"; +import SlidingPane from "react-sliding-pane"; +import { extractComicArchive } from "../../../actions/fileops.actions"; +import { analyzeImage } from "../../../actions/fileops.actions"; export const ArchiveOperations = (props): ReactElement => { const { data } = props; @@ -14,23 +16,52 @@ export const ArchiveOperations = (props): ReactElement => { (state: RootState) => state.fileOps.extractedComicBookArchive, ); + const imageAnalysisResult = useSelector( + (state: RootState) => state.fileOps.imageAnalysisResults, + ); + const dispatch = useDispatch(); const unpackComicArchive = useCallback(() => { dispatch( - extractComicArchive( - data.rawFileDetails.containedIn + - "/" + - data.rawFileDetails.name + - data.rawFileDetails.extension, - { - extractTarget: "book", - targetExtractionFolder: - "./userdata/expanded/" + data.rawFileDetails.name, - extractionMode: "all", - }, - ), + extractComicArchive(data.rawFileDetails.filePath, { + extractTarget: "book", + targetExtractionFolder: + "./userdata/expanded/" + data.rawFileDetails.name, + extractionMode: "all", + }), ); }, [dispatch, data]); + + // sliding panel config + const [visible, setVisible] = useState(false); + const [slidingPanelContentId, setSlidingPanelContentId] = useState(""); + // current image + const [currentImage, setCurrentImage] = useState([]); + + // sliding panel init + const contentForSlidingPanel = { + imageAnalysis: { + content: () => { + return ( +
+
{currentImage}
+
+              {JSON.stringify(imageAnalysisResult, null, 2)}
+            
+
+ ); + }, + }, + }; + + // sliding panel handlers + const openImageAnalysisPanel = useCallback((imageFilePath) => { + setSlidingPanelContentId("imageAnalysis"); + dispatch(analyzeImage(imageFilePath)); + setCurrentImage(imageFilePath); + setVisible(true); + }, []); + return (
+ setVisible(false)} + title={"Image Analysis"} + width={"600px"} + > + {slidingPanelContentId !== "" && + contentForSlidingPanel[slidingPanelContentId].content()} + ); }; diff --git a/src/client/components/Cover.tsx b/src/client/components/Cover.tsx index 960db10..715da70 100644 --- a/src/client/components/Cover.tsx +++ b/src/client/components/Cover.tsx @@ -18,7 +18,7 @@ export const Cover = forwardRef( ...style, }; - return
; + return
; }, ); diff --git a/src/client/components/DnD.tsx b/src/client/components/DnD.tsx index d0df85a..e47182f 100644 --- a/src/client/components/DnD.tsx +++ b/src/client/components/DnD.tsx @@ -18,7 +18,7 @@ import { import { Grid } from "./Grid"; import { SortableCover } from "./SortableCover"; import { Cover } from "./Cover"; -import { map } from "lodash"; +import { map } from "lodash"; export const DnD = (data) => { const [items, setItems] = useState(data.data); @@ -57,7 +57,25 @@ export const DnD = (data) => { {map(items, (url, index) => { - return ; + return ( +
+ +
data.onClickHandler(url)} + > +
+ {index} + + + + + + +
+
+
+ ); })}
diff --git a/src/client/components/VolumeDetail/PotentialLibraryMatches.tsx b/src/client/components/VolumeDetail/PotentialLibraryMatches.tsx index 5f6acaf..a3d5a04 100644 --- a/src/client/components/VolumeDetail/PotentialLibraryMatches.tsx +++ b/src/client/components/VolumeDetail/PotentialLibraryMatches.tsx @@ -9,6 +9,7 @@ import { escapePoundSymbol } from "../../shared/utils/formatting.utils"; import prettyBytes from "pretty-bytes"; const PotentialLibraryMatches = (props): ReactElement => { + console.log(props); const dispatch = useDispatch(); const comicBooks = useSelector( (state: RootState) => state.comicInfo.comicBooksDetails, @@ -64,7 +65,7 @@ const PotentialLibraryMatches = (props): ReactElement => { ); }) ) : ( -
asdasd
+
No matches found in library.
)}
); diff --git a/src/client/components/VolumeDetail/VolumeDetail.tsx b/src/client/components/VolumeDetail/VolumeDetail.tsx index c720962..cb94ec0 100644 --- a/src/client/components/VolumeDetail/VolumeDetail.tsx +++ b/src/client/components/VolumeDetail/VolumeDetail.tsx @@ -19,7 +19,7 @@ const VolumeDetails = (props): ReactElement => { default: 6, 1100: 4, 700: 3, - 600: 2, + 500: 2, }; // sliding panel config const [visible, setVisible] = useState(false); diff --git a/src/client/constants/action-types.ts b/src/client/constants/action-types.ts index dbabc89..964fde9 100644 --- a/src/client/constants/action-types.ts +++ b/src/client/constants/action-types.ts @@ -53,7 +53,8 @@ export const CV_ISSUES_FOR_VOLUME_IN_LIBRARY_SUCCESS = "CV_ISSUES_FOR_VOLUME_IN_LIBRARY_SUCCESS"; export const CV_ISSUES_FOR_VOLUME_IN_LIBRARY_UPDATED = "CV_ISSUES_FOR_VOLUME_IN_LIBRARY_UPDATED"; -export const CV_ISSUES_MATCHES_IN_LIBRARY_FETCHED = "CV_ISSUES_MATCHES_IN_LIBRARY_FETCHED"; +export const CV_ISSUES_MATCHES_IN_LIBRARY_FETCHED = + "CV_ISSUES_MATCHES_IN_LIBRARY_FETCHED"; // extracted comic archive export const IMS_COMIC_BOOK_ARCHIVE_EXTRACTION_SUCCESS = @@ -63,6 +64,12 @@ export const IMS_COMIC_BOOK_ARCHIVE_EXTRACTION_CALL_IN_PROGRESS = export const IMS_COMIC_BOOK_ARCHIVE_EXTRACTION_CALL_FAILED = "IMS_COMIC_BOOK_ARCHIVE_EXTRACTION_CALL_FAILED"; +// Image file stats +export const IMG_ANALYSIS_CALL_IN_PROGRESS = "IMG_ANALYSIS_CALL_IN_PROGRESS"; +export const IMG_ANALYSIS_DATA_FETCH_SUCCESS = + "IMG_ANALYSIS_DATA_FETCH_SUCCESS"; +export const IMG_ANALYSIS_DATA_FETCH_ERROR = "IMG_ANALYSIS_DATA_FETCH_ERROR"; + // AirDC++ export const AIRDCPP_SEARCH_IN_PROGRESS = "AIRDCPP_SEARCH_IN_PROGRESS"; export const AIRDCPP_SEARCH_RESULTS_ADDED = "AIRDCPP_SEARCH_RESULTS_ADDED"; diff --git a/src/client/constants/endpoints.ts b/src/client/constants/endpoints.ts index 81b05ab..bafad19 100644 --- a/src/client/constants/endpoints.ts +++ b/src/client/constants/endpoints.ts @@ -57,6 +57,13 @@ export const SETTINGS_SERVICE_BASE_URI = hostURIBuilder({ apiPath: "/api/settings", }); +export const IMAGETRANSFORMATION_SERVICE_BASE_URI = hostURIBuilder({ + protocol: "http", + host: process.env.UNDERLYING_HOSTNAME || "localhost", + port: "3000", + apiPath: "/api/imagetransformation", +}); + export const SOCKET_BASE_URI = hostURIBuilder({ protocol: "ws", host: process.env.UNDERLYING_HOSTNAME || "localhost", diff --git a/src/client/reducers/comicinfo.reducer.js b/src/client/reducers/comicinfo.reducer.js index 9cd02de..825379c 100644 --- a/src/client/reducers/comicinfo.reducer.js +++ b/src/client/reducers/comicinfo.reducer.js @@ -1,4 +1,4 @@ -import { isEmpty, extend, each, matches } from "lodash"; +import { isEmpty } from "lodash"; import { CV_API_CALL_IN_PROGRESS, CV_SEARCH_SUCCESS, @@ -7,12 +7,9 @@ import { IMS_COMIC_BOOKS_DB_OBJECTS_FETCHED, IMS_COMIC_BOOK_DB_OBJECT_CALL_IN_PROGRESS, CV_ISSUES_METADATA_CALL_IN_PROGRESS, - CV_ISSUES_METADATA_FETCH_SUCCESS, - CV_ISSUES_FOR_VOLUME_IN_LIBRARY_UPDATED, CV_ISSUES_MATCHES_IN_LIBRARY_FETCHED, CV_ISSUES_FOR_VOLUME_IN_LIBRARY_SUCCESS, } from "../constants/action-types"; -import { refineQuery } from "filename-parser"; const initialState = { searchResults: [], @@ -79,23 +76,24 @@ function comicinfoReducer(state = initialState, action) { case CV_ISSUES_MATCHES_IN_LIBRARY_FETCHED: const updatedState = [...state.issuesForVolume]; - const matches = action.matches.filter( - (match) => !isEmpty(match.hits.hits), - ); - matches.forEach((match) => { - state.issuesForVolume.forEach((issue, idx) => { - issue.matches = []; - match.hits.hits.forEach((hit) => { - if ( - parseInt(issue.issue_number, 10) === - hit._source.inferredMetadata.issue.number - ) { - issue.matches.push(hit); - } - }); + action.matches.map((match) => { + updatedState.map((issue, idx) => { + let matches = []; + if (!isEmpty(match.hits.hits)) { + return match.hits.hits.map((hit) => { + if ( + parseInt(issue.issue_number, 10) === + hit._source.inferredMetadata.issue.number + ) { + matches.push(hit); + const updatedIssueResult = { ...issue, matches }; + updatedState[idx] = updatedIssueResult; + } + }); + } }); }); - + console.log(updatedState); return { ...state, issuesForVolume: updatedState, diff --git a/src/client/reducers/fileops.reducer.ts b/src/client/reducers/fileops.reducer.ts index b9aa940..ec89a23 100644 --- a/src/client/reducers/fileops.reducer.ts +++ b/src/client/reducers/fileops.reducer.ts @@ -17,9 +17,13 @@ import { LS_IMPORT, LS_COVER_EXTRACTED, LS_COMIC_ADDED, + IMG_ANALYSIS_CALL_IN_PROGRESS, + IMG_ANALYSIS_DATA_FETCH_SUCCESS, } from "../constants/action-types"; const initialState = { IMSCallInProgress: false, + IMGCallInProgress: false, + imageAnalysisResults: {}, comicBookExtractionInProgress: false, comicBookMetadata: [], comicVolumeGroups: [], @@ -135,6 +139,18 @@ function fileOpsReducer(state = initialState, action) { ...state, }; } + case IMG_ANALYSIS_CALL_IN_PROGRESS: { + return { + ...state, + IMGCallInProgress: true, + }; + } + case IMG_ANALYSIS_DATA_FETCH_SUCCESS: { + return { + ...state, + imageAnalysisResults: action.result, + }; + } default: return state; }