You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
227 lines
5.9 KiB
227 lines
5.9 KiB
import { faBookmark as farBookmark } from "@fortawesome/free-regular-svg-icons";
|
|
import {
|
|
faBookmark,
|
|
faBriefcase,
|
|
faHistory,
|
|
faUser,
|
|
} from "@fortawesome/free-solid-svg-icons";
|
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
|
import React, { FunctionComponent, useCallback, useMemo } from "react";
|
|
import { Badge, ButtonGroup } from "react-bootstrap";
|
|
import { Column, TableOptions, TableUpdater } from "react-table";
|
|
import { ProvidersApi } from "../../apis";
|
|
import {
|
|
ActionButton,
|
|
AsyncStateOverlay,
|
|
EpisodeHistoryModal,
|
|
GroupTable,
|
|
SubtitleToolModal,
|
|
useShowModal,
|
|
} from "../../components";
|
|
import { ManualSearchModal } from "../../components/modals/ManualSearchModal";
|
|
import { BuildKey } from "../../utilites";
|
|
import { SubtitleAction } from "./components";
|
|
|
|
interface Props {
|
|
episodes: AsyncState<Item.Episode[]>;
|
|
update: () => void;
|
|
}
|
|
|
|
const download = (item: any, result: SearchResultType) => {
|
|
item = item as Item.Episode;
|
|
const { language, hearing_impaired, forced, provider, subtitle } = result;
|
|
return ProvidersApi.downloadEpisodeSubtitle(
|
|
item.sonarrSeriesId,
|
|
item.sonarrEpisodeId,
|
|
{
|
|
language,
|
|
hi: hearing_impaired,
|
|
forced,
|
|
provider,
|
|
subtitle,
|
|
}
|
|
);
|
|
};
|
|
|
|
const Table: FunctionComponent<Props> = ({ episodes, update }) => {
|
|
const showModal = useShowModal();
|
|
|
|
const columns: Column<Item.Episode>[] = useMemo<Column<Item.Episode>[]>(
|
|
() => [
|
|
{
|
|
accessor: "monitored",
|
|
Cell: (row) => {
|
|
return (
|
|
<FontAwesomeIcon
|
|
title={row.value ? "monitored" : "unmonitored"}
|
|
icon={row.value ? faBookmark : farBookmark}
|
|
></FontAwesomeIcon>
|
|
);
|
|
},
|
|
},
|
|
{
|
|
accessor: "season",
|
|
Cell: (row) => {
|
|
return `Season ${row.value}`;
|
|
},
|
|
},
|
|
{
|
|
Header: "Episode",
|
|
accessor: "episode",
|
|
},
|
|
{
|
|
Header: "Title",
|
|
accessor: "title",
|
|
className: "text-nowrap",
|
|
},
|
|
{
|
|
Header: "Audio",
|
|
accessor: "audio_language",
|
|
Cell: (row) => {
|
|
return row.value.map((v) => (
|
|
<Badge variant="secondary" key={v.code2}>
|
|
{v.name}
|
|
</Badge>
|
|
));
|
|
},
|
|
},
|
|
{
|
|
Header: "Subtitles",
|
|
accessor: "missing_subtitles",
|
|
Cell: ({ row }) => {
|
|
const episode = row.original;
|
|
|
|
const seriesid = episode.sonarrSeriesId;
|
|
|
|
const elements = useMemo(() => {
|
|
const episodeid = episode.sonarrEpisodeId;
|
|
|
|
const missing = episode.missing_subtitles.map((val, idx) => (
|
|
<SubtitleAction
|
|
missing
|
|
key={BuildKey(idx, val.code2, "missing")}
|
|
seriesid={seriesid}
|
|
episodeid={episodeid}
|
|
subtitle={val}
|
|
></SubtitleAction>
|
|
));
|
|
|
|
const existing = episode.subtitles.filter(
|
|
(val) =>
|
|
episode.missing_subtitles.findIndex(
|
|
(v) => v.code2 === val.code2
|
|
) === -1
|
|
);
|
|
|
|
const subtitles = existing.map((val, idx) => (
|
|
<SubtitleAction
|
|
key={BuildKey(idx, val.code2, "valid")}
|
|
seriesid={seriesid}
|
|
episodeid={episodeid}
|
|
subtitle={val}
|
|
></SubtitleAction>
|
|
));
|
|
|
|
return [...missing, ...subtitles];
|
|
}, [episode, seriesid]);
|
|
|
|
return elements;
|
|
},
|
|
},
|
|
{
|
|
Header: "Actions",
|
|
accessor: "sonarrEpisodeId",
|
|
Cell: ({ row, externalUpdate }) => {
|
|
return (
|
|
<ButtonGroup>
|
|
<ActionButton
|
|
icon={faUser}
|
|
onClick={() => {
|
|
externalUpdate && externalUpdate(row, "manual-search");
|
|
}}
|
|
></ActionButton>
|
|
<ActionButton
|
|
icon={faHistory}
|
|
onClick={() => {
|
|
externalUpdate && externalUpdate(row, "history");
|
|
}}
|
|
></ActionButton>
|
|
<ActionButton
|
|
icon={faBriefcase}
|
|
onClick={() => {
|
|
externalUpdate && externalUpdate(row, "tools");
|
|
}}
|
|
></ActionButton>
|
|
</ButtonGroup>
|
|
);
|
|
},
|
|
},
|
|
],
|
|
[]
|
|
);
|
|
|
|
const updateRow = useCallback<TableUpdater<Item.Episode>>(
|
|
(row, modalKey: string) => {
|
|
if (modalKey === "tools") {
|
|
showModal(modalKey, [row.original]);
|
|
} else {
|
|
showModal(modalKey, row.original);
|
|
}
|
|
},
|
|
[showModal]
|
|
);
|
|
|
|
const maxSeason = useMemo(
|
|
() =>
|
|
episodes.data.reduce<number>(
|
|
(prev, curr) => Math.max(prev, curr.season),
|
|
0
|
|
),
|
|
[episodes]
|
|
);
|
|
|
|
const options: TableOptions<Item.Episode> = useMemo(() => {
|
|
return {
|
|
columns,
|
|
data: episodes.data,
|
|
externalUpdate: updateRow,
|
|
initialState: {
|
|
sortBy: [
|
|
{ id: "season", desc: true },
|
|
{ id: "episode", desc: true },
|
|
],
|
|
groupBy: ["season"],
|
|
expanded: {
|
|
[`season:${maxSeason}`]: true,
|
|
},
|
|
},
|
|
};
|
|
}, [episodes, columns, maxSeason, updateRow]);
|
|
|
|
return (
|
|
<React.Fragment>
|
|
<AsyncStateOverlay state={episodes}>
|
|
{() => (
|
|
<GroupTable
|
|
emptyText="No Episode Found For This Series"
|
|
{...options}
|
|
></GroupTable>
|
|
)}
|
|
</AsyncStateOverlay>
|
|
<SubtitleToolModal
|
|
modalKey="tools"
|
|
size="lg"
|
|
update={update}
|
|
></SubtitleToolModal>
|
|
<EpisodeHistoryModal modalKey="history" size="lg"></EpisodeHistoryModal>
|
|
<ManualSearchModal
|
|
modalKey="manual-search"
|
|
onDownload={update}
|
|
onSelect={download}
|
|
></ManualSearchModal>
|
|
</React.Fragment>
|
|
);
|
|
};
|
|
|
|
export default Table;
|