From 2952be5ca3beca1c5670dbe2f6383dc403e986fa Mon Sep 17 00:00:00 2001 From: "Kayashov.SM" Date: Mon, 29 Sep 2025 23:10:33 +0400 Subject: [PATCH] =?UTF-8?q?=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D0=BE=20=D0=BF=D0=BE=D0=BB=D0=BD=D0=BE=D0=B5=20=D0=B0?= =?UTF-8?q?=D0=BF=D0=B8=20sonarr?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/CardCompact.js | 2 +- src/components/CardExtend.js | 2 +- src/components/RecommendationModal.js | 61 ++++++--------------------- src/components/contentCard.js | 7 ++- src/components/monitors.js | 33 +++++++++++++++ src/components/saveRequest.js | 49 +++++++++++++++++++++ src/pages/HomePage.js | 2 + src/pages/SearchPage.js | 57 +++++++++++++++---------- 8 files changed, 139 insertions(+), 74 deletions(-) create mode 100644 src/components/monitors.js create mode 100644 src/components/saveRequest.js diff --git a/src/components/CardCompact.js b/src/components/CardCompact.js index 71f5ccf..2bb2162 100644 --- a/src/components/CardCompact.js +++ b/src/components/CardCompact.js @@ -12,7 +12,7 @@ const CardCompact = ({item}) => { {item.title}
Год: {item.year}
-
Рейтинг: {cc.rating}
+ {cc.rating || cc.rating !== 0 ?
Рейтинг: {cc.rating}
: null} {cc.status ?
{cc.status}
: null} {cc.size ?
Размер: {cc.size} Gb
: null} {(item?.statistics?.seasonCount ?
Сезоны: {item.statistics.seasonCount}
: null)} diff --git a/src/components/CardExtend.js b/src/components/CardExtend.js index 78e0c63..2220f04 100644 --- a/src/components/CardExtend.js +++ b/src/components/CardExtend.js @@ -23,7 +23,7 @@ const CardExtended = ({item, selectHandle}) => { - + diff --git a/src/components/RecommendationModal.js b/src/components/RecommendationModal.js index aaad867..358dfd0 100644 --- a/src/components/RecommendationModal.js +++ b/src/components/RecommendationModal.js @@ -1,18 +1,9 @@ import React, {useEffect, useState} from 'react'; import {Button, Col, Container, FormCheck, FormSelect, Modal, Row} from 'react-bootstrap'; import {useToast} from "../hooks/useToast"; -import {radarr} from "../contexts/client"; - -const movieMonitor = [ - { - id: "movieAndCollection", - name: "Все части" - }, - { - id: "movieOnly", - name: "Только этот фильм" - } -] +import {radarr, sonarr} from "../contexts/client"; +import {movieMonitor, tvMonitor} from "./monitors"; +import {createRequest} from "./saveRequest"; const RecommendationModal = ({show, handleClose, item, serial, handleSave}) => { const [movieQuality, setMovieQuality] = useState([]) @@ -21,9 +12,11 @@ const RecommendationModal = ({show, handleClose, item, serial, handleSave}) => { const [film, setFilm] = useState(false) const {addToast} = useToast(); const rating = item?.ratings?.imdb?.value ?? null; + const client = serial ? sonarr() : radarr(); + const monitorArray = serial ? tvMonitor : movieMonitor; useEffect(() => { - radarr().get("api/v3/qualityprofile") + client.get("api/v3/qualityprofile") .then((r) => { setMovieQuality(r.data) }) @@ -32,43 +25,15 @@ const RecommendationModal = ({show, handleClose, item, serial, handleSave}) => { }, []); const handleSubmit = () => { - const request = !serial ? createMovieRequest : null; - request() + createRequest(monitor, quality, item, serial, film) .then(res => { - radarr().post("api/v3/movie", res) + client.post(`api/v3/${serial ? "series" : "movie"}`, res) .then(() => handleSave(item, serial)) - .catch((err) => addToast(err, 'danger')); + .catch((err) => addToast(err.message, 'danger')); }) .catch((err) => addToast(err, 'danger')); } - const createMovieRequest = async () => { - if (!monitor) { - // eslint-disable-next-line no-throw-literal - throw 'Проверьте пункт отслеживания'; - } - if (!quality) { - // eslint-disable-next-line no-throw-literal - throw 'Необходимо указать качество'; - } - let request = item; - request.id = 0; - request.monitored = true; - request.qualityProfileId = quality; - request.minimumAvailability = "released" - request.addOptions = { - monitor: monitor, - searchForMovie: true - } - const folders = await radarr().get("api/v3/rootFolder"); - request.rootFolderPath = folders.data.find((d) => d.path.includes(film ? "film" : "mult")).path; - - const tags = await radarr().get("api/v3/tag"); - request.tags = tags.data.filter((t) => t.label === (film ? "film" : "mult")).map((t) => t.id) - - return request; - } - if (!item) { return null; } @@ -82,7 +47,7 @@ const RecommendationModal = ({show, handleClose, item, serial, handleSave}) => { - {item.title} + {item.title}/
@@ -101,7 +66,7 @@ const RecommendationModal = ({show, handleClose, item, serial, handleSave}) => { setFilm(!film)}/>

Фильм

- @@ -111,7 +76,7 @@ const RecommendationModal = ({show, handleClose, item, serial, handleSave}) => {

Что отслеживать

setMonitor(e.target.value)}> - {movieMonitor.map((m) => )} + {monitorArray.map((m) => )} @@ -131,7 +96,7 @@ const RecommendationModal = ({show, handleClose, item, serial, handleSave}) => { - diff --git a/src/components/contentCard.js b/src/components/contentCard.js index 6c07656..51667f8 100644 --- a/src/components/contentCard.js +++ b/src/components/contentCard.js @@ -1,12 +1,15 @@ export const getCardContent = (item) => { let result = { - image: item?.images?.length > 0 ? item.images[0]?.remoteUrl : null, - rating: item.ratings?.imdb?.value ?? null, + rating: item.ratings?.imdb?.value ?? item.ratings.value ?? null, status: null, size: null, series: null, }; + if(item.images?.length > 0) { + result.image = item.images.find(image => image.coverType === "poster").remoteUrl; + } + if (item.sizeOnDisk) { result.size = item.sizeOnDisk / 1000000000; } diff --git a/src/components/monitors.js b/src/components/monitors.js new file mode 100644 index 0000000..b7d4e23 --- /dev/null +++ b/src/components/monitors.js @@ -0,0 +1,33 @@ +export const movieMonitor = [ + { + id: "movieAndCollection", + name: "Все части" + }, + { + id: "movieOnly", + name: "Только этот фильм" + } +] + +export const tvMonitor = [ + { + id: "all", + name: "Все серии" + }, + { + id: "future", + name: "Новые серии" + }, + { + id: "firstSeason", + name: "Первый сезон" + }, + { + id: "lastSeason", + name: "Последний сезон" + }, + { + id: "latestSeason", + name: "Последние сезоны" + }, +] \ No newline at end of file diff --git a/src/components/saveRequest.js b/src/components/saveRequest.js new file mode 100644 index 0000000..a5283c4 --- /dev/null +++ b/src/components/saveRequest.js @@ -0,0 +1,49 @@ +import {sonarr} from "../contexts/client"; + +export const createRequest = async (monitor, quality, item, serial, film) => { + if (!monitor) { + // eslint-disable-next-line no-throw-literal + throw 'Проверьте пункт отслеживания'; + } + if (!quality) { + // eslint-disable-next-line no-throw-literal + throw 'Необходимо указать качество'; + } + let request = item; + request.monitored = true; + request.qualityProfileId = quality; + + var addOptions = { + monitor: monitor, + } + + const folders = await sonarr().get("api/v3/rootFolder"); + request.rootFolderPath = !serial ? folders.data.find((d) => d.path.includes(film ? "film" : "mult")).path : folders.data[0].path; + + if (!serial) { + request.id = 0; + request.minimumAvailability = "released" + addOptions = { + ...addOptions, + searchForMovie: true + } + const tags = await sonarr().get("api/v3/tag"); + request.tags = tags.data.filter((t) => t.label === (film ? "film" : "mult")).map((t) => t.id) + } else { + request.seasonFolder = true; + addOptions = { + ...addOptions, + searchForMissingEpisodes: true, + searchForCutoffUnmetEpisodes: false + } + + delete request.minimumAvailability; + delete request.monitorNewItems; + delete request.id; + } + + delete request.serial; + request.addOptions = addOptions; + + return request; +} \ No newline at end of file diff --git a/src/pages/HomePage.js b/src/pages/HomePage.js index 0d87aa8..81102fb 100644 --- a/src/pages/HomePage.js +++ b/src/pages/HomePage.js @@ -12,6 +12,8 @@ const Home = () => { const [tv, setTv] = useState([]); const {addToast} = useToast(); + // На будущую реализацию удаления + // api/v3/movie/68?deleteFiles=true&addImportExclusion=false useEffect(() => { setLoadingMovies(true); setLoadingTv(true); diff --git a/src/pages/SearchPage.js b/src/pages/SearchPage.js index d707d1c..d8807e2 100644 --- a/src/pages/SearchPage.js +++ b/src/pages/SearchPage.js @@ -3,15 +3,14 @@ import CardExtend from "../components/CardExtend"; import {Alert, Container, InputGroup, Row} from "react-bootstrap"; import RecommendationModal from "../components/RecommendationModal"; import {useToast} from "../hooks/useToast"; -import {radarr} from "../contexts/client"; +import {radarr, sonarr} from "../contexts/client"; export function Search() { const [query, setQuery] = useState(''); + const [serial, setSerial] = useState(false); const [loadMovies, setLoadMovies] = useState(false); - // const [loadTv, setLoadTv] = useState(false); - const [errorMovies, setErrorMovies] = useState(false); - // const [errorTv, setErrorTv] = useState(false); - // const [resultTv, setResultTv] = useState([]); + const [loadTv, setLoadTv] = useState(false); + const [resultTv, setResultTv] = useState([]); const [resultMovies, setResultMovies] = useState([]); const [showModal, setShowModal] = useState(false); const [selectItem, setSelectItem] = useState({}); @@ -23,22 +22,43 @@ export function Search() { } setLoadMovies(true); radarr().get(`api/v3/movie/lookup?term=${query}}`) - .then((r) => setResultMovies(r.data)) - .catch((err) => setErrorMovies(err)) + .then((r) => { + const movies = r.data.map((m) => { + return { + ...m, + serial: false + } + }) + setResultMovies(movies); + }) + .catch((err) => addToast(err, 'alert')) .finally(() => setLoadMovies(false)); + setLoadTv(true); + sonarr().get(`api/v3/series/lookup?term=${query}}`) + .then((r) => { + const series = r.data.map((s) => { + return { + ...s, + serial: true + } + }) + setResultTv(series) + }).catch((err) => addToast(err, 'alert')) + .finally(() => setLoadTv(false)); + // eslint-disable-next-lin }, [query]); const handleChange = (item) => { setSelectItem(item); setShowModal(true); + setSerial(item.serial); } const result = useMemo(() => { - let array = resultMovies; - // array.concat(resultTv); + let array = resultMovies.concat(resultTv); return array.sort((a, b) => a.title.localeCompare(b.title)); - }, [resultMovies]); + }, [resultMovies, resultTv]); const handleCloseModal = () => { setShowModal(false); @@ -49,15 +69,12 @@ export function Search() { let nState = resultMovies; nState = nState.filter(i => i.title !== item.title && i.year !== item.year); - const func = setResultMovies; + const func = serial ? setResultTv : setResultMovies; func(nState); addToast(`${item.title} добавлен в список загрузок`, 'success'); handleCloseModal(); } - // Используем хук useAxios для обработки запросов - // const {response, loading, errorTv} = useAxios('/api/search', 'get', {query}); - return (

Поиск

@@ -73,28 +90,24 @@ export function Search() { - {loadMovies &&
Загрузка...
} - - {errorMovies && ( - {errorMovies} - )} + {(loadMovies || loadTv) &&
Загрузка...
} {result && result.length > 0 && (

Результаты поиска

- {resultMovies.map(result => )}
)} - {(!loadMovies && !errorMovies && (!resultMovies || !result.length)) && ( + {((!loadMovies && (!resultMovies || !result.length) && (!loadTv && (!resultTv || !result.length))) && Ничего не найдено )} -
);