+
+ {title(torrent)}
+ {torrent.status.name}
+
+
dispatch(removeTorrent(torrent.status.id))}
- >
-
+ />
+
);
diff --git a/frontend/js/components/torrents/list/torrentGroup.js b/frontend/js/components/torrents/list/torrentGroup.js
new file mode 100644
index 0000000..9e3d568
--- /dev/null
+++ b/frontend/js/components/torrents/list/torrentGroup.js
@@ -0,0 +1,49 @@
+import React from "react";
+import PropTypes from "prop-types";
+import { useSelector } from "react-redux";
+
+import { Torrent } from "./torrent";
+import { Poster } from "./poster";
+
+export const TorrentGroup = ({ torrentKey }) => {
+ const torrents = useSelector((state) =>
+ state.torrents.torrents.get(torrentKey)
+ );
+
+ if (torrents.length === 0) {
+ return null;
+ }
+
+ const title = (torrent) => {
+ switch (torrent.type) {
+ case "movie":
+ return torrent.video.title;
+ case "episode":
+ return torrent.video.show_title;
+ default:
+ return "Files";
+ }
+ };
+
+ return (
+
No torrents
@@ -16,8 +18,8 @@ export const Torrents = () => {
return (
- {torrents.map((torrent, index) => (
-
+ {torrentsKeys.map((key) => (
+
))}
);
diff --git a/frontend/js/reducers/torrents.js b/frontend/js/reducers/torrents.js
index 3dee8bb..920ba1b 100644
--- a/frontend/js/reducers/torrents.js
+++ b/frontend/js/reducers/torrents.js
@@ -3,10 +3,42 @@ import { produce } from "immer";
const defaultState = {
fetching: false,
searching: false,
- torrents: [],
+ torrents: new Map(),
+ count: 0,
searchResults: [],
};
+// Group the torrents by imdb id
+const formatTorrents = (input) => {
+ let torrents = new Map();
+ if (!input) {
+ return torrents;
+ }
+
+ input.forEach((t) => {
+ let key;
+ switch (t.type) {
+ case "movie":
+ key = t.video ? t.video.imdb_id : "unknown";
+ break;
+ case "episode":
+ key = t.video ? t.video.show_imdb_id : "unknown";
+ break;
+ default:
+ key = "unknown";
+ break;
+ }
+
+ if (!torrents.has(key)) {
+ torrents.set(key, []);
+ }
+
+ torrents.get(key).push(t);
+ });
+
+ return torrents;
+};
+
export default (state = defaultState, action) =>
produce(state, (draft) => {
switch (action.type) {
@@ -16,7 +48,8 @@ export default (state = defaultState, action) =>
case "TORRENTS_FETCH_FULFILLED":
draft.fetching = false;
- draft.torrents = action.payload.response.data;
+ draft.torrents = formatTorrents(action.payload.response.data);
+ draft.count = action.payload.response.data.length;
break;
case "TORRENTS_SEARCH_PENDING":
diff --git a/frontend/js/utils.js b/frontend/js/utils.js
index e6ba682..bbb19c0 100644
--- a/frontend/js/utils.js
+++ b/frontend/js/utils.js
@@ -18,6 +18,9 @@ export const prettyDurationFromMinutes = (runtime) => {
const pad = (d) => (d < 10 ? "0" + d.toString() : d.toString());
+export const prettyEpisodeNameWithoutShow = (season, episode) =>
+ `S${pad(season)}E${pad(episode)}`;
+
export const prettyEpisodeName = (showName, season, episode) =>
`${showName} S${pad(season)}E${pad(episode)}`;