🍼 Cleaned up the OPDS feed
This commit is contained in:
@@ -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": {
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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 <Masonry> */
|
||||
@@ -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 <Masonry> */
|
||||
@@ -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%;
|
||||
|
||||
@@ -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 = (
|
||||
<tbody>
|
||||
{map(airDCPPSearchResults, ({ result }, idx) => {
|
||||
return (
|
||||
<tr key={idx}>
|
||||
<tr
|
||||
key={idx}
|
||||
className={!isNil(result.dupe) ? "dupe-search-result" : ""}
|
||||
>
|
||||
<td>
|
||||
<p className="mb-2">
|
||||
{result.type.id === "directory" ? (
|
||||
@@ -136,9 +140,13 @@ export const AcquisitionPanel = (
|
||||
) : null}{" "}
|
||||
{ellipsize(result.name, 70)}
|
||||
</p>
|
||||
|
||||
<dl>
|
||||
<dd>
|
||||
<div className="tags">
|
||||
{!isNil(result.dupe) ? (
|
||||
<span className="tag is-warning">Dupe</span>
|
||||
) : null}
|
||||
<span className="tag is-light is-info">
|
||||
{result.users.user.nicks}
|
||||
</span>
|
||||
@@ -169,17 +177,17 @@ export const AcquisitionPanel = (
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<a
|
||||
onClick={() =>
|
||||
downloadDCPPResult(
|
||||
searchInstance.id,
|
||||
result.id,
|
||||
props.comicBookMetadata._id,
|
||||
)
|
||||
}
|
||||
>
|
||||
<i className="fas fa-file-download"></i>
|
||||
</a>
|
||||
<a
|
||||
onClick={() =>
|
||||
downloadDCPPResult(
|
||||
searchInstance.id,
|
||||
result.id,
|
||||
props.comicBookMetadata._id,
|
||||
)
|
||||
}
|
||||
>
|
||||
<i className="fas fa-file-download"></i>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
|
||||
@@ -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) => (
|
||||
<div className="column is-one-quarter">
|
||||
{JSON.stringify(props.downloadProgressTick)}
|
||||
<progress
|
||||
className="progress is-small is-success"
|
||||
value={props.downloaded_bytes}
|
||||
max={props.size}
|
||||
>
|
||||
{(parseInt(props.downloaded_bytes) / parseInt(props.size)) * 100}%
|
||||
</progress>
|
||||
<dl>
|
||||
<dt>{props.name}</dt>
|
||||
<dd>
|
||||
{prettyBytes(props.downloaded_bytes)} of
|
||||
{prettyBytes(props.size)}
|
||||
</dd>
|
||||
<dd>
|
||||
{prettyBytes(props.speed)} per second. Time left:
|
||||
{parseInt(props.seconds_left) / 60}
|
||||
</dd>
|
||||
<dd>{props.target}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
);
|
||||
|
||||
const Bundles = (props) => {
|
||||
console.log(props)
|
||||
const ProgressTick = (props) => {
|
||||
console.log("tick", props);
|
||||
return (
|
||||
<div>
|
||||
<dl>
|
||||
{!isNil(props.data) &&
|
||||
props.data &&
|
||||
map(props.data, (bundle) => (
|
||||
<span key={bundle.id}>
|
||||
<dt>{bundle.name}</dt>
|
||||
<dd>{bundle.target}</dd>
|
||||
<dd>{bundle.size}</dd>
|
||||
</span>
|
||||
))}
|
||||
</dl>
|
||||
<div className="column is-half">
|
||||
{JSON.stringify(props.data.downloadProgressTick)}
|
||||
<progress
|
||||
className="progress is-small is-success"
|
||||
value={props.data.downloaded_bytes}
|
||||
max={props.data.size}
|
||||
>
|
||||
{(parseInt(props.data.downloaded_bytes) / parseInt(props.data.size)) *
|
||||
100}
|
||||
%
|
||||
</progress>
|
||||
<div className="card">
|
||||
<div className="card-content is-size-7">
|
||||
<dl>
|
||||
<dt>{props.data.name}</dt>
|
||||
<dd>
|
||||
{prettyBytes(props.data.downloaded_bytes)} of{" "}
|
||||
{prettyBytes(props.data.size)}
|
||||
</dd>
|
||||
<dd>{prettyBytes(props.data.speed)} per second.</dd>
|
||||
<dd>
|
||||
Time left:
|
||||
{parseInt(props.data.seconds_left) / 60}
|
||||
</dd>
|
||||
<dd>{props.data.target}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
return !isNil(props.data) ? <Bundles data={props.data} /> : null;
|
||||
const Bundles = (props) => {
|
||||
console.log(props);
|
||||
return (
|
||||
<table className="table is-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Filename</th>
|
||||
<th>Size</th>
|
||||
<th>Time</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{!isNil(props.data) &&
|
||||
props.data &&
|
||||
map(props.data, (bundle) => (
|
||||
<tr key={bundle.id}>
|
||||
<td>
|
||||
<h5>{ellipsize(bundle.name, 58)}</h5>
|
||||
<span className="is-size-7">{bundle.target}</span>
|
||||
</td>
|
||||
<td>{prettyBytes(bundle.size)}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
};
|
||||
|
||||
return !isNil(props.data) ? (
|
||||
<>
|
||||
{!isNil(downloadProgressTick) ? (
|
||||
<ProgressTick data={downloadProgressTick} />
|
||||
) : null}
|
||||
<Bundles data={bundles} />
|
||||
</>
|
||||
) : null;
|
||||
};
|
||||
|
||||
export default DownloadsPanel;
|
||||
|
||||
@@ -32,7 +32,7 @@ export const VolumeGroups = (): ReactElement => {
|
||||
map(volumeGroups.data, (group) => {
|
||||
if (!isNil(group)) {
|
||||
return (
|
||||
<div className="stack">
|
||||
<div className="stack" key={group.results.id}>
|
||||
<img src={group.results.image.small_url} />
|
||||
<div className="content">
|
||||
<div className="stack-title">
|
||||
|
||||
@@ -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", () => {
|
||||
|
||||
@@ -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<Entry>({
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
15
yarn.lock
15
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"
|
||||
|
||||
Reference in New Issue
Block a user