diff --git a/internal/database/release.go b/internal/database/release.go
index 7c6a6e2..38cfff9 100644
--- a/internal/database/release.go
+++ b/internal/database/release.go
@@ -211,7 +211,7 @@ func (repo *ReleaseRepo) findReleases(ctx context.Context, tx *Tx, params domain
}
queryBuilder := repo.db.squirrel.
- Select("r.id", "r.filter_status", "r.rejections", "r.indexer", "r.filter", "r.protocol", "r.info_url", "r.download_url", "r.title", "r.torrent_name", "r.size", "r.timestamp",
+ Select("r.id", "r.filter_status", "r.rejections", "r.indexer", "r.filter", "r.protocol", "r.info_url", "r.download_url", "r.title", "r.torrent_name", "r.size", "r.category", "r.season", "r.episode", "r.year", "r.resolution", "r.source", "r.codec", "r.container", "r.release_group", "r.timestamp",
"ras.id", "ras.status", "ras.action", "ras.action_id", "ras.type", "ras.client", "ras.filter", "ras.filter_id", "ras.release_id", "ras.rejections", "ras.timestamp").
Column(sq.Alias(countQuery, "page_total")).
From("release r").
@@ -245,17 +245,22 @@ func (repo *ReleaseRepo) findReleases(ctx context.Context, tx *Tx, params domain
var rls domain.Release
var ras domain.ReleaseActionStatus
- var rlsindexer, rlsfilter, infoUrl, downloadUrl sql.NullString
+ var rlsindexer, rlsfilter, infoUrl, downloadUrl, codec sql.NullString
var rasId, rasFilterId, rasReleaseId, rasActionId sql.NullInt64
var rasStatus, rasAction, rasType, rasClient, rasFilter sql.NullString
var rasRejections []sql.NullString
var rasTimestamp sql.NullTime
- if err := rows.Scan(&rls.ID, &rls.FilterStatus, pq.Array(&rls.Rejections), &rlsindexer, &rlsfilter, &rls.Protocol, &infoUrl, &downloadUrl, &rls.Title, &rls.TorrentName, &rls.Size, &rls.Timestamp, &rasId, &rasStatus, &rasAction, &rasActionId, &rasType, &rasClient, &rasFilter, &rasFilterId, &rasReleaseId, pq.Array(&rasRejections), &rasTimestamp, &countItems); err != nil {
+ if err := rows.Scan(&rls.ID, &rls.FilterStatus, pq.Array(&rls.Rejections), &rlsindexer, &rlsfilter, &rls.Protocol, &infoUrl, &downloadUrl, &rls.Title, &rls.TorrentName, &rls.Size, &rls.Category, &rls.Season, &rls.Episode, &rls.Year, &rls.Resolution, &rls.Source, &codec, &rls.Container, &rls.Group, &rls.Timestamp, &rasId, &rasStatus, &rasAction, &rasActionId, &rasType, &rasClient, &rasFilter, &rasFilterId, &rasReleaseId, pq.Array(&rasRejections), &rasTimestamp, &countItems); err != nil {
return res, 0, 0, errors.Wrap(err, "error scanning row")
}
+ //for _, codec := range codecs {
+ // rls.Codec = append(rls.Codec, codec.String)
+ //
+ //}
+
ras.ID = rasId.Int64
ras.Status = domain.ReleasePushStatus(rasStatus.String)
ras.Action = rasAction.String
@@ -291,6 +296,7 @@ func (repo *ReleaseRepo) findReleases(ctx context.Context, tx *Tx, params domain
rls.ActionStatus = make([]domain.ReleaseActionStatus, 0)
rls.InfoURL = infoUrl.String
rls.DownloadURL = downloadUrl.String
+ rls.Codec = strings.Split(codec.String, ",")
// only add ActionStatus if it's not empty
if ras.ID > 0 {
diff --git a/internal/domain/release.go b/internal/domain/release.go
index bca09c7..75e96f7 100644
--- a/internal/domain/release.go
+++ b/internal/domain/release.go
@@ -59,7 +59,7 @@ type Release struct {
TorrentTmpFile string `json:"-"`
TorrentDataRawBytes []byte `json:"-"`
TorrentHash string `json:"-"`
- TorrentName string `json:"torrent_name"` // full release name
+ TorrentName string `json:"name"` // full release name
Size uint64 `json:"size"`
Title string `json:"title"` // Parsed title
Description string `json:"-"`
@@ -538,32 +538,6 @@ func (r *Release) HasMagnetUri() bool {
return r.MagnetURI != ""
}
-type magnetRoundTripper struct{}
-
-func (rt *magnetRoundTripper) RoundTrip(r *http.Request) (*http.Response, error) {
- if r.URL.Scheme == "magnet" {
- responseBody := r.URL.String()
- respReader := io.NopCloser(strings.NewReader(responseBody))
-
- resp := &http.Response{
- Status: http.StatusText(http.StatusOK),
- StatusCode: http.StatusOK,
- Body: respReader,
- ContentLength: int64(len(responseBody)),
- Header: map[string][]string{
- "Content-Type": {"text/plain"},
- "Location": {responseBody},
- },
- Proto: "HTTP/2.0",
- ProtoMajor: 2,
- }
-
- return resp, nil
- }
-
- return http.DefaultTransport.RoundTrip(r)
-}
-
func (r *Release) ResolveMagnetUri(ctx context.Context) error {
if r.MagnetURI == "" {
return nil
diff --git a/web/src/components/data-table/Cells.tsx b/web/src/components/data-table/Cells.tsx
index b6a71c4..fab165b 100644
--- a/web/src/components/data-table/Cells.tsx
+++ b/web/src/components/data-table/Cells.tsx
@@ -7,33 +7,110 @@ import * as React from "react";
import { toast } from "react-hot-toast";
import { formatDistanceToNowStrict } from "date-fns";
import { useMutation, useQueryClient } from "@tanstack/react-query";
+import { CellProps } from "react-table";
import { ArrowPathIcon, CheckIcon } from "@heroicons/react/24/solid";
-import { ArrowDownTrayIcon, ArrowTopRightOnSquareIcon } from "@heroicons/react/24/outline";
import { ExternalLink } from "../ExternalLink";
-import { ClockIcon, XMarkIcon, NoSymbolIcon } from "@heroicons/react/24/outline";
+import {
+ ClockIcon,
+ XMarkIcon,
+ NoSymbolIcon,
+ ArrowDownTrayIcon,
+ ArrowTopRightOnSquareIcon, DocumentTextIcon
+} from "@heroicons/react/24/outline";
import { APIClient } from "@api/APIClient";
-import { classNames, simplifyDate } from "@utils";
+import {classNames, humanFileSize, simplifyDate} from "@utils";
import { filterKeys } from "@screens/filters/List";
import Toast from "@components/notifications/Toast";
import { RingResizeSpinner } from "@components/Icons";
import { Tooltip } from "@components/tooltips/Tooltip";
-interface CellProps {
- value: string;
-}
-
-interface LinksCellProps {
- value: Release;
-}
-
-export const AgeCell = ({ value }: CellProps) => (
-
- {formatDistanceToNowStrict(new Date(value), { addSuffix: false })}
+export const NameCell = (props: CellProps
) => (
+
+
+
+ {String(props.cell.value)}
+
+
+ Category: {props.row.original.category}
+ Size: {humanFileSize(props.row.original.size)}
+ Misc: {`${props.row.original.resolution} ${props.row.original.source} ${props.row.original.codec ?? ""} ${props.row.original.container}`}
+
+
);
-export const IndexerCell = ({ value }: CellProps) => (
+export const LinksCell = (props: CellProps) => {
+ return (
+
+
+
}
+ title="Details"
+ >
+
+ {props.row.original.name}
+ {props.row.original.indexer}
+ {props.row.original.protocol}
+ {props.row.original.implementation}
+ {props.row.original.category}
+ {props.row.original.uploader}
+ {humanFileSize(props.row.original.size)}
+ {props.row.original.title}
+ {props.row.original.year > 0 && {props.row.original.year.toString()}}
+ {props.row.original.season > 0 &&
+ {props.row.original.season.toString()}}
+ {props.row.original.episode > 0 &&
+ {props.row.original.episode.toString()}}
+ {props.row.original.resolution}
+ {props.row.original.source}
+ {props.row.original.codec}
+ {props.row.original.hdr}
+ {props.row.original.group}
+ {props.row.original.container}
+ {props.row.original.origin}
+
+
+
+ {props.row.original.download_url && (
+
+
+
+ )}
+ {props.row.original.info_url && (
+
+
+
+ )}
+
+ );
+};
+
+export const AgeCell = ({value}: CellProps) => (
+
+ {formatDistanceToNowStrict(new Date(value), {addSuffix: false})}
+
+);
+
+export const IndexerCell = ({value}: CellProps) => (
(
);
-export const TitleCell = ({ value }: CellProps) => (
+export const TitleCell = ({value}: CellProps) => (
(
);
-export const LinksCell = ({ value }: LinksCellProps) => {
- return (
-
- {value.download_url && (
-
-
-
- )}
- {value.info_url && (
-
-
-
- )}
-
- );
-};
diff --git a/web/src/components/data-table/index.tsx b/web/src/components/data-table/index.tsx
index d146d4d..0a12b33 100644
--- a/web/src/components/data-table/index.tsx
+++ b/web/src/components/data-table/index.tsx
@@ -4,4 +4,4 @@
*/
export { Button, PageButton } from "./Buttons";
-export { AgeCell, IndexerCell, TitleCell, ReleaseStatusCell, LinksCell } from "./Cells";
+export { AgeCell, IndexerCell, NameCell, TitleCell, ReleaseStatusCell, LinksCell } from "./Cells";
diff --git a/web/src/screens/dashboard/ActivityTable.tsx b/web/src/screens/dashboard/ActivityTable.tsx
index 17e2572..76c7fd1 100644
--- a/web/src/screens/dashboard/ActivityTable.tsx
+++ b/web/src/screens/dashboard/ActivityTable.tsx
@@ -168,7 +168,7 @@ export const ActivityTable = () => {
},
{
Header: "Release",
- accessor: "torrent_name",
+ accessor: "name",
Cell: DataTable.TitleCell
},
{
@@ -183,7 +183,7 @@ export const ActivityTable = () => {
Filter: SelectColumnFilter,
filter: "includes"
}
- ], []);
+ ] as Column[], []);
const { isLoading, data } = useSuspenseQuery({
queryKey: ["dash_recent_releases"],
@@ -213,7 +213,7 @@ export const ActivityTable = () => {
const randomNames = RandomLinuxIsos(data.data.length);
const newData: Release[] = data.data.map((item, index) => ({
...item,
- torrent_name: `${randomNames[index]}.iso`,
+ name: `${randomNames[index]}.iso`,
indexer: index % 2 === 0 ? "distrowatch" : "linuxtracker"
}));
setModifiedData(newData);
diff --git a/web/src/screens/releases/ReleaseTable.tsx b/web/src/screens/releases/ReleaseTable.tsx
index 6adf6cb..583bb2a 100644
--- a/web/src/screens/releases/ReleaseTable.tsx
+++ b/web/src/screens/releases/ReleaseTable.tsx
@@ -6,7 +6,7 @@
import React, { useState } from "react";
import { useLocation } from "react-router-dom";
import { useSuspenseQuery } from "@tanstack/react-query";
-import { CellProps, Column, useFilters, usePagination, useSortBy, useTable } from "react-table";
+import { Column, useFilters, usePagination, useSortBy, useTable } from "react-table";
import {
ChevronDoubleLeftIcon,
ChevronDoubleRightIcon,
@@ -23,8 +23,6 @@ import * as Icons from "@components/Icons";
import * as DataTable from "@components/data-table";
import { IndexerSelectColumnFilter, PushStatusSelectColumnFilter, SearchColumnFilter } from "./Filters";
-import { classNames } from "@utils";
-import { Tooltip } from "@components/tooltips/Tooltip";
export const releaseKeys = {
all: ["releases"] as const,
@@ -94,27 +92,8 @@ export const ReleaseTable = () => {
},
{
Header: "Release",
- accessor: "torrent_name",
- Cell: (props: CellProps) => {
- return (
-
-
-
- {String(props.cell.value)}
-
-
-
- );
- },
+ accessor: "name",
+ Cell: DataTable.NameCell,
Filter: SearchColumnFilter
},
{
@@ -156,7 +135,7 @@ export const ReleaseTable = () => {
const randomNames = RandomLinuxIsos(data.data.length);
const newData: Release[] = data.data.map((item, index) => ({
...item,
- torrent_name: `${randomNames[index]}.iso`,
+ name: `${randomNames[index]}.iso`,
indexer: index % 2 === 0 ? "distrowatch" : "linuxtracker"
}));
setModifiedData(newData);
diff --git a/web/src/types/Release.d.ts b/web/src/types/Release.d.ts
index da33b2b..7dd03da 100644
--- a/web/src/types/Release.d.ts
+++ b/web/src/types/Release.d.ts
@@ -10,11 +10,27 @@ interface Release {
indexer: string;
filter: string;
protocol: string;
+ implementation: string;
+ name: string;
title: string;
size: number;
raw: string;
info_url: string;
download_url: string;
+ category: string;
+ group: string;
+ season: number;
+ episode: number;
+ year: number;
+ resolution: string;
+ codec: string;
+ source: string;
+ container: string;
+ hdr: string;
+ uploader: string;
+ origin: string;
+ // freeleech: boolean;
+ // freeleech_percent:number;
timestamp: Date
action_status: ReleaseActionStatus[]
}
diff --git a/web/src/utils/index.ts b/web/src/utils/index.ts
index c98b44e..996501a 100644
--- a/web/src/utils/index.ts
+++ b/web/src/utils/index.ts
@@ -84,6 +84,34 @@ export const get = (obj: T, path: string|Array, defValue?: string) => {
return result === undefined ? defValue : result;
};
+const UNITS = ['byte', 'kilobyte', 'megabyte', 'gigabyte', 'terabyte', 'petabyte']
+const BYTES_PER_KB = 1000
+
+
+/**
+ * Format bytes as human-readable text.
+ *
+ * @param sizeBytes Number of bytes.
+ *
+ * @return Formatted string.
+ */
+export function humanFileSize(sizeBytes: number | bigint): string {
+ let size = Math.abs(Number(sizeBytes))
+
+ let u = 0
+ while (size >= BYTES_PER_KB && u < UNITS.length - 1) {
+ size /= BYTES_PER_KB
+ ++u
+ }
+
+ return new Intl.NumberFormat([], {
+ style: 'unit',
+ unit: UNITS[u],
+ unitDisplay: 'short',
+ maximumFractionDigits: 1,
+ }).format(size)
+}
+
export const RandomLinuxIsos = (count: number) => {
const linuxIsos = [
"ubuntu-20.04.4-lts-focal-fossa-desktop-amd64-secure-boot",