Compare commits

...

10 Commits

12 changed files with 321 additions and 194 deletions

View File

@@ -1,33 +1,23 @@
import React from 'react'; import React from 'react';
import {Card} from 'react-bootstrap'; import {Card} from 'react-bootstrap';
import {getCardContent} from "./contentCard";
const descriptionDraw = (text) => {
return (
<>
{text} <br/>
</>
)
}
const CardCompact = ({item}) => { const CardCompact = ({item}) => {
console.log(item); const cc = getCardContent(item);
const image = item?.images?.length > 0 ? item.images[0]?.remoteUrl : null;
const rating = item.ratings?.imdb?.value ?? null;
const status = (item.monitored || item.hasFile) ? (
item.hasFile ? "Статус: Загружен" : "Статус: Загружается"
) : null;
return ( return (
<Card className="mb-3" style={{width: '18rem'}}> <Card className="mb-auto mx-auto" style={{width: '20rem'}}>
{/*<Card.Img variant="top" src={item.remotePoster}/>*/} <Card.Img variant="top" src={cc.image}/>
<Card.Img variant="top" src={image}/>
<Card.Body> <Card.Body>
<Card.Title>{item.title}</Card.Title> <Card.Title>{item.title}</Card.Title>
<Card.Text> <Card.Body>
{descriptionDraw(`Год: ${item.year}`)} <div>Год: {item.year} <br/></div>
{descriptionDraw(`Рейтинг: ${rating}`)} {cc.rating || cc.rating !== 0 ? <div>Рейтинг: {cc.rating} <br/></div> : null}
{item.sizeOnDisk ? descriptionDraw(`Размер: ${(item.sizeOnDisk / 1000000000).toFixed(2)} Gb`) : null} {cc.status ? <div>{cc.status}<br/></div> : null}
{status ? descriptionDraw(status) : null} {cc.size ? <div>Размер: {cc.size} Gb</div> : null}
</Card.Text> {(item?.statistics?.seasonCount ? <div>Сезоны: {item.statistics.seasonCount}</div> : null)}
{cc.series ? <div>Серии: {cc.series}</div> : null}
</Card.Body>
</Card.Body> </Card.Body>
</Card> </Card>
); );

View File

@@ -1,20 +1,21 @@
import React from 'react'; import React from 'react';
import {Button, Card, Col, Row} from 'react-bootstrap'; import {Button, Card, Col, Row} from 'react-bootstrap';
import {getCardContent} from "./contentCard";
const CardExtended = ({item, selectHandle}) => { const CardExtended = ({item, selectHandle}) => {
const rating = item.ratings?.imdb?.value ?? null; const cc = getCardContent(item);
return ( return (
<Card className="mb-3" style={{width: '100%'}} onClick={() => selectHandle(item)}> <Card className="mb-3" style={{width: '100%'}} onClick={() => selectHandle(item)}>
<Row> <Row>
<Col md={3}> <Col md={3}>
<Card.Img variant="top" src={item.remotePoster} style={{marginTop: '1rem'}}/> <Card.Img variant="top" src={cc.image} style={{marginTop: '1rem'}}/>
</Col> </Col>
<Col md={9}> <Col md={9}>
<Card.Body> <Card.Body>
<Card.Title>{item.title}</Card.Title> <Card.Title>{item.title}</Card.Title>
<Card.Text> <Card.Text>
<strong>Год:</strong> {item.year}<br/> <strong>Год:</strong> {item.year}<br/>
<strong>Рейтинг:</strong> {rating}<br/> <strong>Рейтинг:</strong> {cc.rating}<br/>
{item.overview} {item.overview}
</Card.Text> </Card.Text>
</Card.Body> </Card.Body>
@@ -22,7 +23,7 @@ const CardExtended = ({item, selectHandle}) => {
</Row> </Row>
<Row> <Row>
<Col style={{justifyContent: 'end'}}> <Col style={{justifyContent: 'end'}}>
<Button variant={'outline-primary'} title={"Download"}>Загрузить</Button> <Button variant={'outline-primary'} disabled title={"Download"}>{item.serial ? "Сериал" : "Фильм"}</Button>
</Col> </Col>
</Row> </Row>
</Card> </Card>

View File

@@ -1,18 +1,9 @@
import React, {useEffect, useState} from 'react'; import React, {useEffect, useState} from 'react';
import {Button, Col, Container, FormCheck, FormSelect, Modal, Row} from 'react-bootstrap'; import {Button, Col, Container, FormCheck, FormSelect, Modal, Row} from 'react-bootstrap';
import axios from "axios";
import {useToast} from "../hooks/useToast"; import {useToast} from "../hooks/useToast";
import {radarr, sonarr} from "../contexts/client";
const movieMonitor = [ import {movieMonitor, tvMonitor} from "./monitors";
{ import {createRequest} from "./saveRequest";
id: "movieAndCollection",
name: "Все части"
},
{
id: "movieOnly",
name: "Только этот фильм"
}
]
const RecommendationModal = ({show, handleClose, item, serial, handleSave}) => { const RecommendationModal = ({show, handleClose, item, serial, handleSave}) => {
const [movieQuality, setMovieQuality] = useState([]) const [movieQuality, setMovieQuality] = useState([])
@@ -20,62 +11,31 @@ const RecommendationModal = ({show, handleClose, item, serial, handleSave}) => {
const [monitor, setMonitor] = useState(null) const [monitor, setMonitor] = useState(null)
const [film, setFilm] = useState(false) const [film, setFilm] = useState(false)
const {addToast} = useToast(); const {addToast} = useToast();
const rating = item?.ratings?.imdb?.value ?? null;
const client = serial ? sonarr() : radarr();
const monitorArray = serial ? tvMonitor : movieMonitor;
useEffect(() => { useEffect(() => {
axios.get(`${process.env.REACT_APP_RADARR_HOST}/api/v3/qualityprofile`, client.get("api/v3/qualityprofile")
{
headers: {
'X-Api-Key': `${process.env.REACT_APP_RADARR_API_KEY}`
}
})
.then((r) => { .then((r) => {
setMovieQuality(r.data) setMovieQuality(r.data)
}) })
.catch(console.error); .catch((err) => addToast(err, 'danger'));
// eslint-disable-next-lint
}, []); }, []);
const handleSubmit = () => { const handleSubmit = () => {
const request = !serial ? createMovieRequest : null; createRequest(monitor, quality, item, serial, film, client)
request()
.then(res => { .then(res => {
axios.post(`${process.env.REACT_APP_RADARR_HOST}/api/v3/movie`, res, client.post(`api/v3/${serial ? "series" : "movie"}`, res)
{headers: {'X-Api-Key': `${process.env.REACT_APP_RADARR_API_KEY}`}})
.then(() => handleSave(item, serial)) .then(() => handleSave(item, serial))
.catch((err) => addToast(err, 'danger')); .catch((err) => addToast(err.message, 'danger'));
// console.log(res)
}) })
.catch((err) => addToast(err, 'danger')); .catch((err) => addToast(err.message, 'danger'));
} }
const createMovieRequest = async () => { if (!item) {
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 axios.get(`${process.env.REACT_APP_RADARR_HOST}/api/v3/rootFolder`,
{headers: {'X-Api-Key': `${process.env.REACT_APP_RADARR_API_KEY}`}});
request.rootFolderPath = folders.data.find((d) => d.path.includes(film ? "film" : "mult")).path;
const tags = await axios.get(`${process.env.REACT_APP_RADARR_HOST}/api/v3/tag`,
{headers: {'X-Api-Key': `${process.env.REACT_APP_RADARR_API_KEY}`}});
request.tags = tags.data.filter((t) => t.label === (film ? "film" : "mult")).map((t) => t.id)
return request;
}
if(!item) {
return null; return null;
} }
@@ -84,58 +44,63 @@ const RecommendationModal = ({show, handleClose, item, serial, handleSave}) => {
<Modal.Header closeButton> <Modal.Header closeButton>
<Modal.Title>{item.title}</Modal.Title> <Modal.Title>{item.title}</Modal.Title>
</Modal.Header> </Modal.Header>
<Modal.Body> <Modal.Body className="d-flex flex-column flex-md-row">
<Container fluid='xl'> <Container fluid>
<Row> <Row className="align-items-start">
<Col sm={{span: 3}}> <Col md={3} className="mb-3">
<img width="100%" className='mb-3' src={item.remotePoster} alt={item.title}/> <img width="100%" className='mb-3' src={item.remotePoster} alt={item.title}/>
</Col> </Col>
<div className="col-9 container text-center"> <Col md={9} className="d-flex flex-column">
<Row> <div>
<div className="col"> <p>{item.overview}</p>
<p>{item.overview}</p> </div>
</div> <div>
</Row> <p>Год выпуска: {item.year}</p>
<Row style={{width: '60%'}}> </div>
<Col> <div className="mb-4">
<p style={{marginRight: '1rem'}}>Мультик</p> <p>Рейтинг: {rating}</p>
<FormCheck type='switch' disabled={serial} style={{marginBottom: '1rem'}} </div>
onChange={() => setFilm(!film)}/> <div>
<p>Фильм</p> <Row>
<FormCheck type='checkbox' disabled={!serial} <Col>
style={{marginBottom: '1rem', marginLeft: '1rem'}} <p style={{marginRight: '1rem'}}>Мультик</p>
label="Сериал"/> <FormCheck type='switch' disabled={serial} style={{marginBottom: '1rem'}}
</Col> onChange={() => setFilm(!film)}/>
</Row> <p>Фильм</p>
<Row> <FormCheck type='checkbox' disabled checked={serial}
<Col> style={{marginBottom: '1rem', marginLeft: '1rem'}}
<p style={{width: '50%', paddingTop: '1rem'}}>Что отслеживать</p> label="Сериал"/>
<FormSelect onChange={(e) => setMonitor(e.target.value)}> </Col>
<option selected value={null}>Выберите пункт из меню</option> </Row>
{movieMonitor.map((m) => <option value={m.id} key={m.id}>{m.name}</option>)} <Row>
</FormSelect> <Col>
</Col> <p style={{width: '50%', paddingTop: '1rem'}}>Что отслеживать</p>
</Row> <FormSelect onChange={(e) => setMonitor(e.target.value)}>
<Row> <option selected value={null}>Выберите пункт из меню</option>
<Col> {monitorArray.map((m) => <option value={m.id} key={m.id}>{m.name}</option>)}
<p style={{width: '50%', paddingTop: '1rem'}}>Качество</p> </FormSelect>
<FormSelect onChange={(e) => setQuality(parseInt(e.target.value))}> </Col>
<option selected value={null}>Выберете качество</option> </Row>
{movieQuality.map((quality) => <option value={quality.id} <Row>
key={quality.id}>{quality.name}</option>)} <Col>
</FormSelect> <p style={{width: '50%', paddingTop: '1rem'}}>Качество</p>
</Col> <FormSelect onChange={(e) => setQuality(parseInt(e.target.value))}>
</Row> <option selected value={null}>Выберете качество</option>
</div> {movieQuality.map((quality) => <option value={quality.id}
key={quality.id}>{quality.name}</option>)}
</FormSelect>
</Col>
</Row>
</div>
</Col>
</Row> </Row>
<Button variant={'primary'} onClick={() => handleSubmit()}>Скачать</Button>
</Container> </Container>
</Modal.Body> </Modal.Body>
<Modal.Footer> <Modal.Footer>
<Button variant="secondary" onClick={() => handleClose()}> <Button variant="secondary" onClick={() => handleClose()}>
Закрыть Закрыть
</Button> </Button>
<Button variant="primary" onClick={() => handleSubmit()}>Скачать</Button>
</Modal.Footer> </Modal.Footer>
</Modal> </Modal>
); );

View File

@@ -1,16 +0,0 @@
export function SceletonCompact() {
return (
<div className="card" aria-hidden="true" style={{width:'18rem', height: '30rem'}}>
<div className="card-img-top" style={{background: 'grey', width: '18rem', height: '30rem'}} />
<div className="card-body">
<h5 className="card-title placeholder-glow">
<span className="placeholder col-6"></span>
</h5>
<p className="card-text placeholder-glow">
<span className="placeholder col-8"></span>
<span className="placeholder col-8"></span>
</p>
</div>
</div>
)
}

View File

@@ -0,0 +1,18 @@
import {Card, CardBody, CardImg, CardText, CardTitle, Placeholder} from "react-bootstrap";
export function SkeletonCompact() {
return (
<Card className="mb-auto" aria-hidden="true" style={{width: '14rem', height: '30rem'}}>
<CardImg variant="top" style={{background: 'grey', width: '14rem', height: '30rem'}}/>
<CardBody>
<CardTitle className="placeholder-glow">
<Placeholder className="col-3"></Placeholder>
</CardTitle>
<CardText className="placeholder-glow">
<Placeholder className="col-6"></Placeholder>
<Placeholder className="col-6"></Placeholder>
</CardText>
</CardBody>
</Card>
)
}

View File

@@ -0,0 +1,33 @@
export const getCardContent = (item) => {
let result = {
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;
}
if (item.monitored || item.hasFile) {
result.status = item.hasFile ? "Статус: Загружен" : "Статус: Загружается"
}
if (item.statistics) {
const stat = item.statistics
result.status = stat.percentOfEpisodes === 100 ? "Статус: Загружен" : "Статус: Загружается"
result.series = `${stat.episodeFileCount}/${stat.totalEpisodeCount}`
if (stat.sizeOnDisk) {
result.size = (stat.sizeOnDisk / 1000000000).toFixed(2);
}
}
return result;
}

View File

@@ -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: "Последние сезоны"
},
]

View File

@@ -0,0 +1,49 @@
export const createRequest = async (monitor, quality, item, serial, film, client) => {
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 client.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 client.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;
}

23
src/contexts/client.js Normal file
View File

@@ -0,0 +1,23 @@
import axios from "axios";
const host = "tracker.kayashov.keenetic.pro";
export const radarr = () => {
const res = axios;
// res.defaults.baseURL = `http://192.168.1.100:30011/`;
res.defaults.baseURL = `${window.location.protocol}//${host}/radarr/`;
res.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
res.defaults.headers.common['X-Api-Key'] = process.env.REACT_APP_RADARR_API_KEY;
res.defaults.headers.common['Accept'] = 'application/json';
return res;
}
export const sonarr = () => {
const res = axios;
res.defaults.baseURL = `${window.location.protocol}//${host}/sonarr/`;
// res.defaults.baseURL = `http://192.168.1.100:30009/`;
res.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
res.defaults.headers.common['X-Api-Key'] = process.env.REACT_APP_SONARR_API_KEY;
res.defaults.headers.common['Accept'] = 'application/json';
return res;
}

View File

@@ -1,23 +1,31 @@
import React, {useEffect, useState} from 'react'; import React, {useEffect, useState} from 'react';
import {Accordion, Badge, Col, Container, Row} from "react-bootstrap"; import {Accordion, Badge, Col, Container, Row} from "react-bootstrap";
import CardCompact from "../components/CardCompact"; import CardCompact from "../components/CardCompact";
import axios from "axios";
import {useToast} from "../hooks/useToast"; import {useToast} from "../hooks/useToast";
import {SceletonCompact} from "../components/SceletonCompact"; import {SkeletonCompact} from "../components/SkeletonCompact";
import {radarr, sonarr} from "../contexts/client";
const Home = () => { const Home = () => {
const [loading, setLoading] = useState(false); const [loadingTv, setLoadingTv] = useState(false);
const [loadingMovies, setLoadingMovies] = useState(false);
const [movies, setMovies] = useState([]); const [movies, setMovies] = useState([]);
// const [series, setSeries] = useState([]); const [tv, setTv] = useState([]);
const {addToast} = useToast(); const {addToast} = useToast();
// На будущую реализацию удаления
// api/v3/movie/68?deleteFiles=true&addImportExclusion=false
useEffect(() => { useEffect(() => {
setLoading(true); setLoadingMovies(true);
axios.get(`${process.env.REACT_APP_RADARR_HOST}/api/v3/movie`, setLoadingTv(true);
{headers: {'X-Api-Key': process.env.REACT_APP_RADARR_API_KEY}}) radarr().get("api/v3/movie")
.then((r) => setMovies(r.data)) .then((r) => setMovies(r.data))
.catch((err) => addToast(err, 'alert')) .catch((err) => addToast(err, 'alert'))
.finally(() => setLoading(false)); .finally(() => setLoadingMovies(false));
sonarr().get("api/v3/series")
.then((r) => setTv(r.data))
.catch((err) => addToast(err, 'alert'))
.finally(() => setLoadingTv(false));
// eslint-disable-next-line // eslint-disable-next-line
}, []); }, []);
@@ -29,17 +37,18 @@ const Home = () => {
Фильмы Фильмы
</Accordion.Header> </Accordion.Header>
<Accordion.Body> <Accordion.Body>
<Row className='row-cols-auto'> <Row>
{loading ? {loadingMovies ?
( (
// Отображаем 10 скелетонов // Отображаем 10 скелетонов
Array.from({length: 10}, (_, index) => ( Array.from({length: 10}, (_, index) => (
<Col key={`skeleton-${index}`}> <Col key={`skeleton-${index}`}>
<SceletonCompact key={index} width={300} height={400}/> <SkeletonCompact key={index} width={300} height={400}/>
</Col> </Col>
)) ))
) )
: movies.map(rec => ( :
movies?.map(rec => (
<Col key={`col-${rec.title}-${rec.year}`}> <Col key={`col-${rec.title}-${rec.year}`}>
<CardCompact item={rec} key={`${rec.title}-${rec.year}`}/> <CardCompact item={rec} key={`${rec.title}-${rec.year}`}/>
</Col> </Col>
@@ -51,7 +60,22 @@ const Home = () => {
<Accordion.Header className="mt-5">Сериалы</Accordion.Header> <Accordion.Header className="mt-5">Сериалы</Accordion.Header>
<Accordion.Body> <Accordion.Body>
<Row> <Row>
<Badge bg="info">Функционал для сериалов пока не реализован</Badge> {loadingTv ?
(
// Отображаем 10 скелетонов
Array.from({length: 10}, (_, index) => (
<Col key={`skeleton-tv-${index}`}>
<SkeletonCompact key={index} width={300} height={400}/>
</Col>
))
)
:
tv.length > 0 ? tv.map(rec => (
<Col key={`col-tv-${rec.title}-${rec.year}`}>
<CardCompact item={rec} key={`${rec.title}-${rec.year}`}/>
</Col>
)) :
<Badge bg="info">Функционал для сериалов пока не реализован</Badge>}
</Row> </Row>
</Accordion.Body> </Accordion.Body>
</Accordion.Item> </Accordion.Item>

View File

@@ -1,10 +1,10 @@
import React, {useEffect, useState} from 'react'; import React, {useEffect, useState} from 'react';
import axios from "axios";
import CardCompact from "../components/CardCompact"; import CardCompact from "../components/CardCompact";
import {SceletonCompact} from "../components/SceletonCompact"; import {SkeletonCompact} from "../components/SkeletonCompact";
import RecommendationModal from "../components/RecommendationModal"; import RecommendationModal from "../components/RecommendationModal";
import {Col, Container, Row} from "react-bootstrap"; import {Col, Container, Row} from "react-bootstrap";
import {useToast} from "../hooks/useToast"; import {useToast} from "../hooks/useToast";
import {radarr} from "../contexts/client";
export function Recommendation() { export function Recommendation() {
const [recommendations, setRecommendations] = useState([]); const [recommendations, setRecommendations] = useState([]);
@@ -15,14 +15,11 @@ export function Recommendation() {
useEffect(() => { useEffect(() => {
setLoading(true); setLoading(true);
axios.get(`${process.env.REACT_APP_RADARR_HOST}/api/v3/importlist/movie?includeRecommendations=true&includeTrending=true&includePopular=true`, radarr().get("api/v3/importlist/movie?includeRecommendations=true&includeTrending=true&includePopular=true")
{ .then((r) => setRecommendations(r.data))
headers: { .catch((err) => addToast(err, 'danger'))
'X-Api-Key': `${process.env.REACT_APP_RADARR_API_KEY}`
}
}).then((r) => setRecommendations(r.data))
.catch((err) => console.log(err))
.finally(() => setLoading(false)); .finally(() => setLoading(false));
// eslint-disable-next-lint
}, []); }, []);
function handleModal(item) { function handleModal(item) {
@@ -45,13 +42,13 @@ export function Recommendation() {
return ( return (
<Container className="text-center"> <Container className="text-center">
<h2 className="mt-3">Рекомендации</h2> <h2 className="mt-3">Рекомендации</h2>
<Row className='row-cols-auto'> <Row>
{loading ? {loading ?
( (
// Отображаем 10 скелетонов // Отображаем 10 скелетонов
Array.from({length: 10}, (_, index) => ( Array.from({length: 10}, (_, index) => (
<Col key={`skeleton-${index}`}> <Col key={`skeleton-${index}`}>
<SceletonCompact key={index} width={300} height={400}/> <SkeletonCompact key={index} width={300} height={400}/>
</Col> </Col>
)) ))
) )
@@ -61,7 +58,8 @@ export function Recommendation() {
</Col> </Col>
))} ))}
</Row> </Row>
<RecommendationModal show={modalView} handleClose={handleCloseModal} item={item} handleSave={handleDownload}/> <RecommendationModal show={modalView} handleClose={handleCloseModal} item={item}
handleSave={handleDownload} serial={false}/>
</Container> </Container>
); );
} }

View File

@@ -1,17 +1,16 @@
import React, {useEffect, useMemo, useState} from 'react'; import React, {useEffect, useMemo, useState} from 'react';
import axios from "axios";
import CardExtend from "../components/CardExtend"; import CardExtend from "../components/CardExtend";
import {Alert, Container, InputGroup, Row} from "react-bootstrap"; import {Alert, Container, InputGroup, Row} from "react-bootstrap";
import RecommendationModal from "../components/RecommendationModal"; import RecommendationModal from "../components/RecommendationModal";
import {useToast} from "../hooks/useToast"; import {useToast} from "../hooks/useToast";
import {radarr, sonarr} from "../contexts/client";
export function Search() { export function Search() {
const [query, setQuery] = useState(''); const [query, setQuery] = useState('');
const [serial, setSerial] = useState(false);
const [loadMovies, setLoadMovies] = useState(false); const [loadMovies, setLoadMovies] = useState(false);
// const [loadTv, setLoadTv] = useState(false); const [loadTv, setLoadTv] = useState(false);
const [errorMovies, setErrorMovies] = useState(false); const [resultTv, setResultTv] = useState([]);
// const [errorTv, setErrorTv] = useState(false);
// const [resultTv, setResultTv] = useState([]);
const [resultMovies, setResultMovies] = useState([]); const [resultMovies, setResultMovies] = useState([]);
const [showModal, setShowModal] = useState(false); const [showModal, setShowModal] = useState(false);
const [selectItem, setSelectItem] = useState({}); const [selectItem, setSelectItem] = useState({});
@@ -22,29 +21,44 @@ export function Search() {
return; return;
} }
setLoadMovies(true); setLoadMovies(true);
axios.get(`${process.env.REACT_APP_RADARR_HOST}/api/v3/movie/lookup?term=${query}}`, radarr().get(`api/v3/movie/lookup?term=${query}}`)
{ .then((r) => {
headers: { const movies = r.data.map((m) => {
// "Content-Type": "application/json", return {
'X-Api-Key': `${process.env.REACT_APP_RADARR_API_KEY}` ...m,
} serial: false
}
})
setResultMovies(movies);
}) })
.then((r) => setResultMovies(r.data)) .catch((err) => addToast(err, 'alert'))
.catch((err) => setErrorMovies(err))
.finally(() => setLoadMovies(false)); .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]); }, [query]);
const handleChange = (item) => { const handleChange = (item) => {
setSelectItem(item); setSelectItem(item);
setShowModal(true); setShowModal(true);
setSerial(item.serial);
} }
const result = useMemo(() => { const result = useMemo(() => {
let array = resultMovies; let array = resultMovies.concat(resultTv);
// array.concat(resultTv);
return array.sort((a, b) => a.title.localeCompare(b.title)); return array.sort((a, b) => a.title.localeCompare(b.title));
}, [resultMovies]); }, [resultMovies, resultTv]);
const handleCloseModal = () => { const handleCloseModal = () => {
setShowModal(false); setShowModal(false);
@@ -55,15 +69,12 @@ export function Search() {
let nState = resultMovies; let nState = resultMovies;
nState = nState.filter(i => i.title !== item.title && i.year !== item.year); nState = nState.filter(i => i.title !== item.title && i.year !== item.year);
const func = setResultMovies; const func = serial ? setResultTv : setResultMovies;
func(nState); func(nState);
addToast(`${item.title} добавлен в список загрузок`, 'success'); addToast(`${item.title} добавлен в список загрузок`, 'success');
handleCloseModal(); handleCloseModal();
} }
// Используем хук useAxios для обработки запросов
// const {response, loading, errorTv} = useAxios('/api/search', 'get', {query});
return ( return (
<Container style={{marginTop: 5}}> <Container style={{marginTop: 5}}>
<h2>Поиск</h2> <h2>Поиск</h2>
@@ -79,27 +90,25 @@ export function Search() {
</InputGroup> </InputGroup>
</form> </form>
{loadMovies && <div className="text-center">Загрузка...</div>} {(loadMovies || loadTv) && <div className="text-center">Загрузка...</div>}
{errorMovies && (
<Alert variant="danger">{errorMovies}</Alert>
)}
{result && result.length > 0 && ( {result && result.length > 0 && (
<div> <div>
<h3>Результаты поиска</h3> <h3>Результаты поиска</h3>
<Row> <Row>
{resultMovies.map(result => <CardExtend item={result} key={result.id} selectHandle={handleChange}/>)} {result.map(result => <CardExtend item={result} key={result.id}
selectHandle={handleChange}/>)}
</Row> </Row>
</div> </div>
)} )}
{(!loadMovies && !errorMovies && (!resultMovies || !result.length)) && ( {((!loadMovies && (!resultMovies || !result.length) && (!loadTv && (!resultTv || !result.length))) &&
<Alert variant='info'> <Alert variant='info'>
Ничего не найдено Ничего не найдено
</Alert> </Alert>
)} )}
<RecommendationModal item={selectItem} handleClose={handleCloseModal} show={showModal} serial={false} handleSave={handleSave}/> <RecommendationModal item={selectItem} handleClose={handleCloseModal} show={showModal} serial={serial}
handleSave={handleSave}/>
</Container> </Container>
); );
} }