import { useState, useEffect, useMemo } from 'react' import { useTranslation } from 'react-i18next' import axios from 'axios' import { TextField, Button, List, ListItem, ListItemText, CircularProgress, Typography, Divider, ListItemSecondaryAction, IconButton, Snackbar, useMediaQuery, Select, MenuItem, FormControl, InputLabel, } from '@material-ui/core' import { CloudDownload as DownloadIcon, ArrowUpward, ArrowDownward } from '@material-ui/icons' import { torznabSearchHost, torrentsHost, settingsHost, searchHost } from 'utils/Hosts' import useOnStandaloneAppOutsideClick from 'utils/useOnStandaloneAppOutsideClick' import { StyledDialog, StyledHeader } from 'style/CustomMaterialUiStyles' import { parseSizeToBytes, formatSizeToClassicUnits } from 'utils/Utils' import { getMoviePosters, shortenTitleForPosterSearch } from 'components/Add/helpers' import { Content } from './style' export default function SearchDialog({ handleClose }) { const { t } = useTranslation() const [query, setQuery] = useState('') const [results, setResults] = useState([]) const [loading, setLoading] = useState(false) const [searched, setSearched] = useState(false) const [adding, setAdding] = useState(false) const [successMsg, setSuccessMsg] = useState('') const [errorMsg, setErrorMsg] = useState('') const [trackers, setTrackers] = useState([]) const [enableRutor, setEnableRutor] = useState(false) const [selectedTracker, setSelectedTracker] = useState(-1) const [sortField, setSortField] = useState('') // '', 'size', 'seeds', 'peers' const [sortDirection, setSortDirection] = useState('desc') // 'asc' or 'desc' const fullScreen = useMediaQuery('@media (max-width:930px)') const isMobile = useMediaQuery('(max-width:600px)') const ref = useOnStandaloneAppOutsideClick(handleClose) useEffect(() => { axios .post(settingsHost(), { action: 'get' }) .then(({ data }) => { if (data) { if (data.TorznabUrls) { setTrackers(data.TorznabUrls) } setEnableRutor(!!data.EnableRutorSearch) } }) .catch(() => {}) }, []) const handleSearch = async () => { if (!query) return setLoading(true) setSearched(true) setResults([]) try { let url = torznabSearchHost() const params = { query } if (selectedTracker === 'rutor') { url = searchHost() } else if (selectedTracker !== -1) { params.index = selectedTracker } const { data } = await axios.get(url, { params }) setResults(data || []) } catch (error) { setErrorMsg(t('Torznab.SearchFailed')) } finally { setLoading(false) } } const handleKeyDown = e => { if (e.key === 'Enter') { handleSearch() } } const handleAdd = async item => { setAdding(true) try { const link = item.Magnet || item.Link if (!link) { setErrorMsg(t('Torznab.NoLinkFound')) return } let poster = item.Poster if (!poster && item.Title) { const query = shortenTitleForPosterSearch(item.Title) if (query) { const urlList = await getMoviePosters(query, 'en') const [firstPosterUrl] = urlList || [] if (firstPosterUrl) poster = firstPosterUrl } } await axios.post(torrentsHost(), { action: 'add', link, title: item.Title, save_to_db: true, poster: poster || '', }) setSuccessMsg(t('Torznab.TorrentAddedSuccessfully')) } catch (error) { setErrorMsg(t('Torznab.FailedToAddTorrent')) } finally { setAdding(false) } } const handleAlertClose = (event, reason) => { if (reason === 'clickaway') { return } setSuccessMsg('') setErrorMsg('') } const toggleSortDirection = () => { setSortDirection(prev => (prev === 'asc' ? 'desc' : 'asc')) } const sortedResults = useMemo(() => { if (!sortField || results.length === 0) return results const sorted = [...results].sort((a, b) => { let aVal let bVal switch (sortField) { case 'size': aVal = parseSizeToBytes(a.Size || '0') bVal = parseSizeToBytes(b.Size || '0') break case 'seeds': aVal = a.Seed || 0 bVal = b.Seed || 0 break case 'peers': aVal = a.Peer || 0 bVal = b.Peer || 0 break default: return 0 } if (aVal === bVal) return 0 return sortDirection === 'asc' ? (aVal < bVal ? -1 : 1) : aVal > bVal ? -1 : 1 }) return sorted }, [results, sortField, sortDirection]) return ( {t('Torznab.SearchTorrents')}
{t('Tracker')} setQuery(e.target.value)} onKeyDown={handleKeyDown} variant='outlined' size='small' fullWidth placeholder={t('Torznab.SearchMoviesShows')} autoFocus />
{searched && results.length > 0 && (
{t('Torznab.SortBy')} {sortField && ( {sortDirection === 'asc' ? : } )}
)}
{searched && results.length === 0 && !loading && ( {t('Torznab.NoResultsFound')} )} {sortedResults.map((item, index) => { const sizeBytes = parseSizeToBytes(item.Size || '0') const formattedSize = formatSizeToClassicUnits(sizeBytes) return (
handleAdd(item)}> {formattedSize} {` • S: ${item.Seed || 0} P: ${item.Peer || 0}`} } primaryTypographyProps={{ style: { whiteSpace: isMobile ? 'normal' : 'inherit', fontSize: isMobile ? '0.9rem' : 'inherit', }, }} secondaryTypographyProps={{ style: { fontSize: isMobile ? '0.75rem' : 'inherit', }, }} /> handleAdd(item)} disabled={adding} size={isMobile ? 'small' : 'medium'} >
) })}
) }