From bbde67bb11ddc88e6044cfa694b17a43d7efb63e Mon Sep 17 00:00:00 2001 From: Rishi Ghan Date: Thu, 23 Sep 2021 18:11:20 -0700 Subject: [PATCH] =?UTF-8?q?=F0=9F=8D=BC=20Cleaned=20up=20the=20OPDS=20feed?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 1 + src/client/actions/airdcpp.actions.tsx | 4 +- src/client/assets/scss/App.scss | 10 +- src/client/components/AcquisitionPanel.tsx | 32 +++-- src/client/components/DownloadsPanel.tsx | 123 ++++++++++++------- src/client/components/VolumeGroups.tsx | 2 +- src/client/context/socket/socket.context.tsx | 3 +- src/server/route/routes/opds.routes.ts | 42 +++++-- yarn.lock | 15 ++- 9 files changed, 158 insertions(+), 74 deletions(-) diff --git a/package.json b/package.json index abac689..abf6ba1 100644 --- a/package.json +++ b/package.json @@ -71,6 +71,7 @@ "websocket": "^1.0.34", "ws": "^7.5.3", "ws-calibre": "https://github.com/bluelovers/ws-calibre", + "xml2js": "^0.4.23", "xregexp": "^5.0.2" }, "devDependencies": { diff --git a/src/client/actions/airdcpp.actions.tsx b/src/client/actions/airdcpp.actions.tsx index db4021d..7df2906 100644 --- a/src/client/actions/airdcpp.actions.tsx +++ b/src/client/actions/airdcpp.actions.tsx @@ -33,7 +33,6 @@ export const search = (data: SearchData) => async (dispatch) => { await SocketService.connect("admin", "password", true); } const instance: SearchInstance = await SocketService.post("search"); - console.log(instance); dispatch({ type: AIRDCPP_SEARCH_IN_PROGRESS, }); @@ -98,7 +97,7 @@ export const search = (data: SearchData) => async (dispatch) => { // Finally, perform the actual search await SocketService.post(`search/${instance.id}/hub_search`, data); } catch (error) { - console.log("ERO", error); + console.log(error); throw error; } }; @@ -185,7 +184,6 @@ export const getBundlesForComic = if (!SocketService.isConnected()) { await SocketService.connect("admin", "password", true); } - // const bundles = await SocketService.get("queue/bundles/0/50"); const comicObject = await axios({ method: "POST", url: "http://localhost:3000/api/import/getComicBookById", diff --git a/src/client/assets/scss/App.scss b/src/client/assets/scss/App.scss index 61a9fd7..2072972 100644 --- a/src/client/assets/scss/App.scss +++ b/src/client/assets/scss/App.scss @@ -35,7 +35,7 @@ $border-color: red; width: auto; .recent-comics-column { - padding-left: 25px; /* gutter size */ + padding-left: 22px; /* gutter size */ background-clip: padding-box; & > div { /* change div to reference your elements you put in */ @@ -82,7 +82,7 @@ $border-color: red; width: auto; } .volumes-grid-column { - padding-left: 25px; /* gutter size */ + padding-left: 22px; /* gutter size */ background-clip: padding-box; & > div { /* change div to reference your elements you put in */ @@ -244,6 +244,11 @@ $border-color: red; } } +// AirDC++ search results +.dupe-search-result { + background: lavender; +} + // Search .search { .main-search-bar { @@ -363,6 +368,7 @@ $border-color: red; margin-bottom: 20px; } +// // progress .progress-indicator-container { height: 100%; diff --git a/src/client/components/AcquisitionPanel.tsx b/src/client/components/AcquisitionPanel.tsx index 711f6e3..cfbdc6f 100644 --- a/src/client/components/AcquisitionPanel.tsx +++ b/src/client/components/AcquisitionPanel.tsx @@ -57,6 +57,7 @@ export const AcquisitionPanel = ( dispatch( downloadAirDCPPItem(searchInstanceId, resultId, comicBookObjectId), ); + // this is to update the download count badge on the downloads tab dispatch(getBundlesForComic(comicBookObjectId)); }, [dispatch], @@ -128,7 +129,10 @@ export const AcquisitionPanel = ( {map(airDCPPSearchResults, ({ result }, idx) => { return ( - +

{result.type.id === "directory" ? ( @@ -136,9 +140,13 @@ export const AcquisitionPanel = ( ) : null}{" "} {ellipsize(result.name, 70)}

+
+ {!isNil(result.dupe) ? ( + Dupe + ) : null} {result.users.user.nicks} @@ -169,17 +177,17 @@ export const AcquisitionPanel = (
- - downloadDCPPResult( - searchInstance.id, - result.id, - props.comicBookMetadata._id, - ) - } - > - - + + downloadDCPPResult( + searchInstance.id, + result.id, + props.comicBookMetadata._id, + ) + } + > + + ); diff --git a/src/client/components/DownloadsPanel.tsx b/src/client/components/DownloadsPanel.tsx index 8dffd6d..c15490c 100644 --- a/src/client/components/DownloadsPanel.tsx +++ b/src/client/components/DownloadsPanel.tsx @@ -1,9 +1,13 @@ import React, { useEffect, ReactElement } from "react"; -import { getDownloadProgress } from "../actions/airdcpp.actions"; +import { + getDownloadProgress, + getBundlesForComic, +} from "../actions/airdcpp.actions"; import { useDispatch, useSelector } from "react-redux"; import { RootState } from "threetwo-ui-typings"; import { isNil, map } from "lodash"; import prettyBytes from "pretty-bytes"; +import ellipsize from "ellipsize"; interface IDownloadsPanelProps { data: any; @@ -16,56 +20,87 @@ export const DownloadsPanel = ( const downloadProgressTick = useSelector( (state: RootState) => state.airdcpp.downloadProgressData, ); + const bundles = useSelector((state: RootState) => { + return state.airdcpp.bundles; + }); + console.log("BANDYA", bundles); const dispatch = useDispatch(); - // useEffect(() => { - // dispatch(getDownloadProgress(props.data._id)); - // }, [dispatch]); + useEffect(() => { + dispatch(getBundlesForComic(props.comicObjectId)); + dispatch(getDownloadProgress(props.comicObjectId)); + }, [dispatch]); - const ProgressTick = (props) => ( -
- {JSON.stringify(props.downloadProgressTick)} - - {(parseInt(props.downloaded_bytes) / parseInt(props.size)) * 100}% - -
-
{props.name}
-
- {prettyBytes(props.downloaded_bytes)} of - {prettyBytes(props.size)} -
-
- {prettyBytes(props.speed)} per second. Time left: - {parseInt(props.seconds_left) / 60} -
-
{props.target}
-
-
- ); - - const Bundles = (props) => { - console.log(props) + const ProgressTick = (props) => { + console.log("tick", props); return ( -
-
- {!isNil(props.data) && - props.data && - map(props.data, (bundle) => ( - -
{bundle.name}
-
{bundle.target}
-
{bundle.size}
-
- ))} -
+
+ {JSON.stringify(props.data.downloadProgressTick)} + + {(parseInt(props.data.downloaded_bytes) / parseInt(props.data.size)) * + 100} + % + +
+
+
+
{props.data.name}
+
+ {prettyBytes(props.data.downloaded_bytes)} of{" "} + {prettyBytes(props.data.size)} +
+
{prettyBytes(props.data.speed)} per second.
+
+ Time left: + {parseInt(props.data.seconds_left) / 60} +
+
{props.data.target}
+
+
+
); }; - return !isNil(props.data) ? : null; + const Bundles = (props) => { + console.log(props); + return ( + + + + + + + + + + {!isNil(props.data) && + props.data && + map(props.data, (bundle) => ( + + + + + ))} + +
FilenameSizeTime
+
{ellipsize(bundle.name, 58)}
+ {bundle.target} +
{prettyBytes(bundle.size)}
+ ); + }; + + return !isNil(props.data) ? ( + <> + {!isNil(downloadProgressTick) ? ( + + ) : null} + + + ) : null; }; export default DownloadsPanel; diff --git a/src/client/components/VolumeGroups.tsx b/src/client/components/VolumeGroups.tsx index b9fb325..4e36c7d 100644 --- a/src/client/components/VolumeGroups.tsx +++ b/src/client/components/VolumeGroups.tsx @@ -32,7 +32,7 @@ export const VolumeGroups = (): ReactElement => { map(volumeGroups.data, (group) => { if (!isNil(group)) { return ( -
+
diff --git a/src/client/context/socket/socket.context.tsx b/src/client/context/socket/socket.context.tsx index 1284d33..793ef75 100644 --- a/src/client/context/socket/socket.context.tsx +++ b/src/client/context/socket/socket.context.tsx @@ -3,6 +3,7 @@ import io, { Socket } from "socket.io-client"; import { SOCKET_BASE_URI } from "../../constants/endpoints"; import { useDispatch } from "react-redux"; import { RMQ_SOCKET_CONNECTED } from "../../constants/action-types"; +import { isNil } from "lodash"; const WebSocketContext = createContext(null); export const WebSocketProvider = ({ children }) => { @@ -10,7 +11,7 @@ export const WebSocketProvider = ({ children }) => { let ws; const dispatch = useDispatch(); - if (!socket) { + if (!isNil(socket)) { socket = io(SOCKET_BASE_URI); socket.on("connect", () => { diff --git a/src/server/route/routes/opds.routes.ts b/src/server/route/routes/opds.routes.ts index 8ac659c..0a3f084 100644 --- a/src/server/route/routes/opds.routes.ts +++ b/src/server/route/routes/opds.routes.ts @@ -10,6 +10,7 @@ import { async as FastGlob } from "@bluelovers/fast-glob/bluebird"; import { Entry, Feed } from "opds-extra/lib/v1"; import { Link } from "opds-extra/lib/v1/core"; import router from "../router"; +import xml2js from "xml2js"; const path_of_books = "/Users/rishi/work/threetwo/src/server/comics"; router.use("/opds", async (req, res, next) => { @@ -18,22 +19,17 @@ router.use("/opds", async (req, res, next) => { title: `title`, subtitle: `subtitle`, icon: "/favicon.ico", - link: { - rel: EnumLinkRel.SELF, - href: "/opds-catalogs/root.xml", - type: "application/atom+xml;profile=opds-catalog;kind=navigation", - } as Link, }), [ async (feed: Feed) => { - feed.id = "bastard12312899lfh-1238989123-1231289812"; + feed.id = "urn:uuid:2853dacf-ed79-42f5-8e8a-a7bb3d1ae6a2"; feed.books = feed.books || []; await FastGlob(["*.cbr", "*.cbz", "*.cb7", "*.cba", "*.cbt"], { cwd: path_of_books, }).each((file, idx) => { const ext = extname(file); const title = basename(file, ext); - const href = encodeURI(`/file/${file}`); + const href = encodeURI(`/api/file/${file}`); const type = lookup(ext) || "application/octet-stream"; const entry = Entry.deserialize({ @@ -49,7 +45,6 @@ router.use("/opds", async (req, res, next) => { }); if (!isUndefined(feed) && !isUndefined(feed.books)) { - console.log("ENTRY", entry) feed.books.push(entry); } }); @@ -59,8 +54,35 @@ router.use("/opds", async (req, res, next) => { ], ).then((feed) => { res.setHeader("Content-Type", "application/xml"); - console.log("FFFEEDD", feed); - return res.end(feed.toXML()); + let data; + xml2js.parseString(feed.toXML(), (err, result) => { + result.feed.link = { + $: { + rel: "self", + href: "/opds-catalogs/root.xml", + type: "application/atom+xml;profile=opds-catalog;kind=navigation", + }, + _: "", + }; + const builder = new xml2js.Builder({ + xmldec: { + version: "1.0", + encoding: "UTF-8", + standalone: false, + }, + }); + data = builder.buildObject(result, { + renderOpts: { + pretty: true, + indent: " ", + newline: "\n", + allowEmpty: true, + }, + }); + // write data to file + console.log(data); + }); + return res.end(data); }); }); diff --git a/yarn.lock b/yarn.lock index 6cca4fd..c99522a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11749,7 +11749,7 @@ sass-loader@^11.0.1: klona "^2.0.4" neo-async "^2.6.2" -sax@~1.2.4: +sax@>=0.6.0, sax@~1.2.4: version "1.2.4" resolved "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz" integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== @@ -14141,11 +14141,24 @@ xml-schema2@^3.0.1: xml-parser "1.2.1" xmlbuilder "15.1.1" +xml2js@^0.4.23: + version "0.4.23" + resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.23.tgz#a0c69516752421eb2ac758ee4d4ccf58843eac66" + integrity sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug== + dependencies: + sax ">=0.6.0" + xmlbuilder "~11.0.0" + xmlbuilder@15.1.1: version "15.1.1" resolved "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz" integrity sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg== +xmlbuilder@~11.0.0: + version "11.0.1" + resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-11.0.1.tgz#be9bae1c8a046e76b31127726347d0ad7002beb3" + integrity sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA== + xmlchars@^2.2.0: version "2.2.0" resolved "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz"