From df2d336b480787cac10f0ec6fb689a7c45c19cc9 Mon Sep 17 00:00:00 2001 From: Rishi Ghan Date: Thu, 10 Feb 2022 01:22:47 -0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=8C=88=20Color=20histograms=20for=20image?= =?UTF-8?q?s,=20along=20with=20stats?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/client/actions/fileops.actions.tsx | 7 +- src/client/assets/scss/App.scss | 11 +++- .../ComicDetail/Tabs/ArchiveOperations.tsx | 20 ++++-- src/client/components/Dashboard.tsx | 22 +++++++ src/client/components/Library/SearchBar.tsx | 2 +- src/client/components/LibraryGrid.tsx | 5 +- src/client/components/shared/Canvas.tsx | 64 +++++++++++++++++++ src/client/constants/action-types.ts | 3 + src/client/reducers/comicinfo.reducer.js | 1 - src/client/reducers/fileops.reducer.ts | 9 ++- yarn.lock | 8 +-- 11 files changed, 133 insertions(+), 19 deletions(-) create mode 100644 src/client/components/shared/Canvas.tsx diff --git a/src/client/actions/fileops.actions.tsx b/src/client/actions/fileops.actions.tsx index a02c085..0301428 100644 --- a/src/client/actions/fileops.actions.tsx +++ b/src/client/actions/fileops.actions.tsx @@ -22,6 +22,7 @@ import { IMG_ANALYSIS_DATA_FETCH_SUCCESS, IMS_COMIC_BOOK_ARCHIVE_EXTRACTION_SUCCESS, IMS_COMIC_BOOK_ARCHIVE_EXTRACTION_CALL_IN_PROGRESS, + FILEOPS_STATE_RESET, } from "../constants/action-types"; import { success } from "react-notification-system-redux"; import { isNil, map } from "lodash"; @@ -258,7 +259,10 @@ export const extractComicArchive = export const analyzeImage = (imageFilePath: string | Buffer) => async (dispatch) => { - console.log(imageFilePath); + dispatch({ + type: FILEOPS_STATE_RESET, + }); + dispatch({ type: IMG_ANALYSIS_CALL_IN_PROGRESS, }); @@ -270,7 +274,6 @@ export const analyzeImage = 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 e7d0e95..9fb2f9c 100644 --- a/src/client/assets/scss/App.scss +++ b/src/client/assets/scss/App.scss @@ -42,11 +42,20 @@ pre { } // Dashboard +// Comic Detail +.stats-palette { + background-color: #fff6de; + display: inline-block; + p { + display: flex; + // padding: 1.5rem 2rem; + } +} .recent-comics-container { display: -webkit-box; /* Not needed if autoprefixing */ display: -ms-flexbox; /* Not needed if autoprefixing */ display: flex; - margin-left: -30px; /* gutter size offset */ + margin-left: -22px; /* gutter size offset */ width: auto; .recent-comics-column { diff --git a/src/client/components/ComicDetail/Tabs/ArchiveOperations.tsx b/src/client/components/ComicDetail/Tabs/ArchiveOperations.tsx index 63075f5..b9a0660 100644 --- a/src/client/components/ComicDetail/Tabs/ArchiveOperations.tsx +++ b/src/client/components/ComicDetail/Tabs/ArchiveOperations.tsx @@ -1,11 +1,12 @@ import React, { ReactElement, useCallback, useState } from "react"; import { useSelector, useDispatch } from "react-redux"; import { DnD } from "../../DnD"; -import { isEmpty } from "lodash"; +import { isEmpty, isNil, isUndefined } from "lodash"; import Sticky from "react-stickynode"; import SlidingPane from "react-sliding-pane"; import { extractComicArchive } from "../../../actions/fileops.actions"; import { analyzeImage } from "../../../actions/fileops.actions"; +import { Canvas } from "../../shared/Canvas"; export const ArchiveOperations = (props): ReactElement => { const { data } = props; @@ -16,9 +17,9 @@ export const ArchiveOperations = (props): ReactElement => { (state: RootState) => state.fileOps.extractedComicBookArchive, ); - const imageAnalysisResult = useSelector( - (state: RootState) => state.fileOps.imageAnalysisResults, - ); + const imageAnalysisResult = useSelector((state: RootState) => { + return state.fileOps.imageAnalysisResults; + }); const dispatch = useDispatch(); const unpackComicArchive = useCallback(() => { @@ -44,9 +45,14 @@ export const ArchiveOperations = (props): ReactElement => { content: () => { return (
-
{currentImage}
-
-              {JSON.stringify(imageAnalysisResult, null, 2)}
+            
{currentImage}
+ {!isEmpty(imageAnalysisResult) ? ( +
+                
+              
+ ) : null} +
+              {JSON.stringify(imageAnalysisResult.analyzedData, null, 2)}
             
); diff --git a/src/client/components/Dashboard.tsx b/src/client/components/Dashboard.tsx index 09f1b5b..b4658b9 100644 --- a/src/client/components/Dashboard.tsx +++ b/src/client/components/Dashboard.tsx @@ -32,6 +32,28 @@ export const Dashboard = (): ReactElement => { {!isEmpty(recentComics) && !isEmpty(recentComics.docs) ? ( <> + {/* stats */} +
+
+
+
+ 113123 files +
+
+ 140 tagged + with ComicVine +
+
+ 1304 with + custom metadata +
+
+
+ +
+ asdasd +
+
{!isNil(volumeGroups) ? : null} diff --git a/src/client/components/Library/SearchBar.tsx b/src/client/components/Library/SearchBar.tsx index b1e28d2..afc52f7 100644 --- a/src/client/components/Library/SearchBar.tsx +++ b/src/client/components/Library/SearchBar.tsx @@ -6,7 +6,7 @@ import { Link } from "react-router-dom"; export const SearchBar = (): ReactElement => { const foo = () => {}; return ( -
+
{ let comicName = ""; if (!isNil(rawFileDetails)) { const encodedFilePath = encodeURI( - `${LIBRARY_SERVICE_HOST}` + - removeLeadingPeriod(rawFileDetails.cover.filePath), + `${LIBRARY_SERVICE_HOST}/${removeLeadingPeriod( + rawFileDetails.cover.filePath, + )}`, ); imagePath = escapePoundSymbol(encodedFilePath); comicName = rawFileDetails.name; diff --git a/src/client/components/shared/Canvas.tsx b/src/client/components/shared/Canvas.tsx new file mode 100644 index 0000000..2252615 --- /dev/null +++ b/src/client/components/shared/Canvas.tsx @@ -0,0 +1,64 @@ +import React, { useEffect, useRef } from "react"; + +export const Canvas = (data) => { + const { colorHistogramData } = data.data; + console.log(data); + const width = 559; + const height = 200; + const pixelRatio = window.devicePixelRatio; + + const canvas = useRef(null); + + useEffect(() => { + const context = canvas.current.getContext("2d"); + + const guideHeight = 8; + const startY = height - guideHeight; + const dx = width / 256; + const dy = startY / colorHistogramData.maxBrightness; + context.lineWidth = dx; + context.fillStyle = "transparent"; + context.fillRect(0, 0, width, height); + + for (let i = 0; i < 256; i++) { + const x = i * dx; + + // Red + context.strokeStyle = "rgba(220,0,0,0.5)"; + context.beginPath(); + context.moveTo(x, startY); + context.lineTo(x, startY - colorHistogramData.r[i] * dy); + context.closePath(); + context.stroke(); + // Green + context.strokeStyle = "rgba(0,210,0,0.5)"; + context.beginPath(); + context.moveTo(x, startY); + context.lineTo(x, startY - colorHistogramData.g[i] * dy); + context.closePath(); + context.stroke(); + // Blue + context.strokeStyle = "rgba(0,0,255,0.5)"; + context.beginPath(); + context.moveTo(x, startY); + context.lineTo(x, startY - colorHistogramData.b[i] * dy); + context.closePath(); + context.stroke(); + + // Guide + context.strokeStyle = "rgb(" + i + ", " + i + ", " + i + ")"; + context.beginPath(); + context.moveTo(x, startY); + context.lineTo(x, height); + context.closePath(); + context.stroke(); + } + }); + + const dw = Math.floor(pixelRatio * width); + const dh = Math.floor(pixelRatio * height); + const style = { width, height }; + return ; +}; + +export default Canvas; diff --git a/src/client/constants/action-types.ts b/src/client/constants/action-types.ts index 964fde9..6720aa8 100644 --- a/src/client/constants/action-types.ts +++ b/src/client/constants/action-types.ts @@ -70,6 +70,9 @@ export const IMG_ANALYSIS_DATA_FETCH_SUCCESS = "IMG_ANALYSIS_DATA_FETCH_SUCCESS"; export const IMG_ANALYSIS_DATA_FETCH_ERROR = "IMG_ANALYSIS_DATA_FETCH_ERROR"; +// fileops cleanup +export const FILEOPS_STATE_RESET = "FILEOPS_STATE_RESET"; + // 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/reducers/comicinfo.reducer.js b/src/client/reducers/comicinfo.reducer.js index 825379c..d1a2e5f 100644 --- a/src/client/reducers/comicinfo.reducer.js +++ b/src/client/reducers/comicinfo.reducer.js @@ -93,7 +93,6 @@ function comicinfoReducer(state = initialState, action) { } }); }); - console.log(updatedState); return { ...state, issuesForVolume: updatedState, diff --git a/src/client/reducers/fileops.reducer.ts b/src/client/reducers/fileops.reducer.ts index ec89a23..c8af742 100644 --- a/src/client/reducers/fileops.reducer.ts +++ b/src/client/reducers/fileops.reducer.ts @@ -5,7 +5,6 @@ import { IMS_RAW_IMPORT_SUCCESSFUL, IMS_RAW_IMPORT_FAILED, IMS_RECENT_COMICS_FETCHED, - IMS_DATA_FETCH_ERROR, IMS_CV_METADATA_IMPORT_SUCCESSFUL, IMS_CV_METADATA_IMPORT_FAILED, IMS_CV_METADATA_IMPORT_CALL_IN_PROGRESS, @@ -19,6 +18,7 @@ import { LS_COMIC_ADDED, IMG_ANALYSIS_CALL_IN_PROGRESS, IMG_ANALYSIS_DATA_FETCH_SUCCESS, + FILEOPS_STATE_RESET, } from "../constants/action-types"; const initialState = { IMSCallInProgress: false, @@ -151,6 +151,13 @@ function fileOpsReducer(state = initialState, action) { imageAnalysisResults: action.result, }; } + + case FILEOPS_STATE_RESET: { + return { + ...state, + imageAnalysisResults: {}, + }; + } default: return state; } diff --git a/yarn.lock b/yarn.lock index c703021..3bc1615 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5805,10 +5805,10 @@ filehound@^1.17.5: moment "^2.29.1" unit-compare "^1.0.1" -filename-parser@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/filename-parser/-/filename-parser-1.0.1.tgz#cd54d9131913dd2bf96c2a1d19edf7c1ee75c4cc" - integrity sha512-MMzuklXc1r4N6uQXg8CQYxoiTX7w6QPeJE73L5lCTSoNR7CftCXHIA6tyINomkvWIUanrlqTB629DyTIqCucEA== +filename-parser@^1.0.2: + version "1.0.4" + resolved "https://registry.yarnpkg.com/filename-parser/-/filename-parser-1.0.4.tgz#dfa2ae09040997e4e4d2063b735edae6e21bfbaa" + integrity sha512-r/cFGGlCFaz2taSlDzOtVWO66WvqPDBv6CtyqKw4GCP7+V3r5D5J0ci3fnYaDm5/GkqWL5aGA6JTbu8e+oKQMA== dependencies: compromise "^13.11.4" compromise-dates "^2.2.1"