mirror of
https://github.com/Ernous/TorrServerJellyfin.git
synced 2025-12-20 05:56:10 +05:00
Merge branch 'addDialog-refactor'
This commit is contained in:
@@ -1,65 +1,46 @@
|
|||||||
import { useEffect, useMemo, useState } from 'react'
|
import { useEffect, useMemo, useState } from 'react'
|
||||||
import Button from '@material-ui/core/Button'
|
import Button from '@material-ui/core/Button'
|
||||||
import TextField from '@material-ui/core/TextField'
|
|
||||||
import Dialog from '@material-ui/core/Dialog'
|
import Dialog from '@material-ui/core/Dialog'
|
||||||
import { torrentsHost, torrentUploadHost } from 'utils/Hosts'
|
import { torrentsHost, torrentUploadHost } from 'utils/Hosts'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { NoImageIcon, AddItemIcon, TorrentIcon } from 'icons'
|
|
||||||
import debounce from 'lodash/debounce'
|
import debounce from 'lodash/debounce'
|
||||||
import { v4 as uuidv4 } from 'uuid'
|
|
||||||
import useChangeLanguage from 'utils/useChangeLanguage'
|
import useChangeLanguage from 'utils/useChangeLanguage'
|
||||||
import { Cancel as CancelIcon } from '@material-ui/icons'
|
|
||||||
import { useDropzone } from 'react-dropzone'
|
|
||||||
import { useMediaQuery } from '@material-ui/core'
|
import { useMediaQuery } from '@material-ui/core'
|
||||||
import parseTorrent from 'parse-torrent'
|
|
||||||
import ptt from 'parse-torrent-title'
|
|
||||||
import CircularProgress from '@material-ui/core/CircularProgress'
|
import CircularProgress from '@material-ui/core/CircularProgress'
|
||||||
|
import usePreviousState from 'utils/usePreviousState'
|
||||||
|
|
||||||
import { checkImageURL, getMoviePosters, chechTorrentSource } from './helpers'
|
import { checkImageURL, getMoviePosters, chechTorrentSource, parseTorrentTitle } from './helpers'
|
||||||
import {
|
import { ButtonWrapper, Content, Header } from './style'
|
||||||
ButtonWrapper,
|
import RightSideComponent from './RightSideComponent'
|
||||||
CancelIconWrapper,
|
import LeftSideComponent from './LeftSideComponent'
|
||||||
ClearPosterButton,
|
|
||||||
PosterLanguageSwitch,
|
|
||||||
Content,
|
|
||||||
Header,
|
|
||||||
IconWrapper,
|
|
||||||
RightSide,
|
|
||||||
Poster,
|
|
||||||
PosterSuggestions,
|
|
||||||
PosterSuggestionsItem,
|
|
||||||
PosterWrapper,
|
|
||||||
LeftSide,
|
|
||||||
LeftSideBottomSectionFileSelected,
|
|
||||||
LeftSideBottomSectionNoFile,
|
|
||||||
LeftSideTopSection,
|
|
||||||
TorrentIconWrapper,
|
|
||||||
RightSideContainer,
|
|
||||||
} from './style'
|
|
||||||
|
|
||||||
export default function AddDialog({ handleClose }) {
|
export default function AddDialog({ handleClose }) {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const [torrentSource, setTorrentSource] = useState('')
|
const [torrentSource, setTorrentSource] = useState('')
|
||||||
const [isTorrentSourceActive, setIsTorrentSourceActive] = useState(false)
|
|
||||||
const [title, setTitle] = useState('')
|
const [title, setTitle] = useState('')
|
||||||
const [posterUrl, setPosterUrl] = useState('')
|
const [posterUrl, setPosterUrl] = useState('')
|
||||||
const [isPosterUrlCorrect, setIsPosterUrlCorrect] = useState(false)
|
const [isPosterUrlCorrect, setIsPosterUrlCorrect] = useState(false)
|
||||||
const [isTorrentSourceCorrect, setIsTorrentSourceCorrect] = useState(false)
|
const [isTorrentSourceCorrect, setIsTorrentSourceCorrect] = useState(false)
|
||||||
const [posterList, setPosterList] = useState()
|
const [posterList, setPosterList] = useState()
|
||||||
const [isUserInteractedWithPoster, setIsUserInteractedWithPoster] = useState(false)
|
const [isUserInteractedWithPoster, setIsUserInteractedWithPoster] = useState(false)
|
||||||
const [isUserInteractedWithTitle, setIsUserInteractedWithTitle] = useState(false)
|
|
||||||
const [currentLang] = useChangeLanguage()
|
const [currentLang] = useChangeLanguage()
|
||||||
const [selectedFile, setSelectedFile] = useState()
|
const [selectedFile, setSelectedFile] = useState()
|
||||||
const [posterSearchLanguage, setPosterSearchLanguage] = useState(currentLang === 'ru' ? 'ru' : 'en')
|
const [posterSearchLanguage, setPosterSearchLanguage] = useState(currentLang === 'ru' ? 'ru' : 'en')
|
||||||
const [isLoadingButton, setIsLoadingButton] = useState(false)
|
const [isLoadingButton, setIsLoadingButton] = useState(false)
|
||||||
|
const [skipDebounce, setSkipDebounce] = useState(false)
|
||||||
|
|
||||||
const fullScreen = useMediaQuery('@media (max-width:930px)')
|
const fullScreen = useMediaQuery('@media (max-width:930px)')
|
||||||
|
|
||||||
const posterSearch = useMemo(
|
const posterSearch = useMemo(
|
||||||
() =>
|
() =>
|
||||||
(movieName, language, settings = {}) => {
|
(movieName, language, { shouldRefreshMainPoster = false } = {}) => {
|
||||||
const { shouldRefreshMainPoster = false } = settings
|
if (!movieName) {
|
||||||
|
setPosterList()
|
||||||
|
removePoster()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
getMoviePosters(movieName, language).then(urlList => {
|
getMoviePosters(movieName, language).then(urlList => {
|
||||||
if (urlList) {
|
if (urlList) {
|
||||||
setPosterList(urlList)
|
setPosterList(urlList)
|
||||||
@@ -85,65 +66,54 @@ export default function AddDialog({ handleClose }) {
|
|||||||
|
|
||||||
const delayedPosterSearch = useMemo(() => debounce(posterSearch, 700), [posterSearch])
|
const delayedPosterSearch = useMemo(() => debounce(posterSearch, 700), [posterSearch])
|
||||||
|
|
||||||
useEffect(() => {
|
const prevTitleState = usePreviousState(title)
|
||||||
if (isUserInteractedWithTitle) return
|
const prevTorrentSourceState = usePreviousState(torrentSource)
|
||||||
|
|
||||||
parseTorrent.remote(selectedFile || torrentSource, (err, parsedTorrent) => {
|
|
||||||
if (err) throw err
|
|
||||||
if (!parsedTorrent.name) return
|
|
||||||
|
|
||||||
const torrentName = ptt.parse(parsedTorrent.name).title
|
|
||||||
const fileInsideTorrentName = parsedTorrent.files ? ptt.parse(parsedTorrent.files[0].name).title : null
|
|
||||||
|
|
||||||
let value = torrentName
|
|
||||||
if (fileInsideTorrentName) {
|
|
||||||
value = torrentName.length < fileInsideTorrentName.length ? torrentName : fileInsideTorrentName
|
|
||||||
}
|
|
||||||
|
|
||||||
setTitle(value)
|
|
||||||
delayedPosterSearch(value, posterSearchLanguage)
|
|
||||||
})
|
|
||||||
}, [selectedFile, delayedPosterSearch, torrentSource, posterSearchLanguage, isUserInteractedWithTitle])
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setIsTorrentSourceCorrect(chechTorrentSource(torrentSource))
|
// if torrentSource is updated then we are checking that source is valid and getting title from the source
|
||||||
|
const torrentSourceChanged = torrentSource !== prevTorrentSourceState
|
||||||
|
|
||||||
if (!torrentSource) {
|
const isCorrectSource = chechTorrentSource(torrentSource)
|
||||||
setPosterUrl('')
|
if (!isCorrectSource) return setIsTorrentSourceCorrect(false)
|
||||||
setPosterList()
|
|
||||||
setIsPosterUrlCorrect(false)
|
setIsTorrentSourceCorrect(true)
|
||||||
|
|
||||||
|
if (torrentSourceChanged) {
|
||||||
|
parseTorrentTitle(selectedFile || torrentSource, newTitle => {
|
||||||
|
if (!newTitle) return
|
||||||
|
|
||||||
|
setSkipDebounce(true)
|
||||||
|
setTitle(newTitle)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}, [torrentSource])
|
}, [prevTorrentSourceState, selectedFile, torrentSource])
|
||||||
|
|
||||||
const handleCapture = files => {
|
useEffect(() => {
|
||||||
const [file] = files
|
// if title exists and title was changed then search poster.
|
||||||
if (!file) return
|
const titleChanged = title !== prevTitleState
|
||||||
|
if (!titleChanged) return
|
||||||
|
|
||||||
setIsUserInteractedWithPoster(false)
|
if (skipDebounce) {
|
||||||
setSelectedFile(file)
|
posterSearch(title, posterSearchLanguage)
|
||||||
setTorrentSource(file.name)
|
setSkipDebounce(false)
|
||||||
}
|
} else {
|
||||||
|
delayedPosterSearch(title, posterSearchLanguage)
|
||||||
const { getRootProps, getInputProps, isDragActive } = useDropzone({ onDrop: handleCapture, accept: '.torrent' })
|
}
|
||||||
|
}, [title, prevTitleState, delayedPosterSearch, posterSearch, posterSearchLanguage, skipDebounce])
|
||||||
|
|
||||||
const removePoster = () => {
|
const removePoster = () => {
|
||||||
setIsPosterUrlCorrect(false)
|
setIsPosterUrlCorrect(false)
|
||||||
setPosterUrl('')
|
setPosterUrl('')
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleTorrentSourceChange = ({ target: { value } }) => setTorrentSource(value)
|
useEffect(() => {
|
||||||
const handleTitleChange = ({ target: { value } }) => {
|
if (!selectedFile && !torrentSource) {
|
||||||
setTitle(value)
|
setTitle('')
|
||||||
delayedPosterSearch(value, posterSearchLanguage)
|
setPosterList()
|
||||||
|
removePoster()
|
||||||
torrentSource && setIsUserInteractedWithTitle(true)
|
setIsUserInteractedWithPoster(false)
|
||||||
}
|
}
|
||||||
const handlePosterUrlChange = ({ target: { value } }) => {
|
}, [selectedFile, torrentSource])
|
||||||
setPosterUrl(value)
|
|
||||||
checkImageURL(value).then(setIsPosterUrlCorrect)
|
|
||||||
setIsUserInteractedWithPoster(!!value)
|
|
||||||
setPosterList()
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleSave = () => {
|
const handleSave = () => {
|
||||||
setIsLoadingButton(true)
|
setIsLoadingButton(true)
|
||||||
@@ -155,10 +125,7 @@ export default function AddDialog({ handleClose }) {
|
|||||||
data.append('file', selectedFile)
|
data.append('file', selectedFile)
|
||||||
title && data.append('title', title)
|
title && data.append('title', title)
|
||||||
posterUrl && data.append('poster', posterUrl)
|
posterUrl && data.append('poster', posterUrl)
|
||||||
axios
|
axios.post(torrentUploadHost(), data).finally(handleClose)
|
||||||
.post(torrentUploadHost(), data)
|
|
||||||
// .then(res => console.log(res))
|
|
||||||
.finally(handleClose)
|
|
||||||
} else {
|
} else {
|
||||||
// link save
|
// link save
|
||||||
axios
|
axios
|
||||||
@@ -167,18 +134,6 @@ export default function AddDialog({ handleClose }) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const clearSelectedFile = () => {
|
|
||||||
setSelectedFile()
|
|
||||||
setTorrentSource('')
|
|
||||||
setIsUserInteractedWithTitle(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
const userChangesPosterUrl = url => {
|
|
||||||
setPosterUrl(url)
|
|
||||||
checkImageURL(url).then(setIsPosterUrlCorrect)
|
|
||||||
setIsUserInteractedWithPoster(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog
|
<Dialog
|
||||||
open
|
open
|
||||||
@@ -191,120 +146,32 @@ export default function AddDialog({ handleClose }) {
|
|||||||
<Header>{t('AddNewTorrent')}</Header>
|
<Header>{t('AddNewTorrent')}</Header>
|
||||||
|
|
||||||
<Content>
|
<Content>
|
||||||
<LeftSide>
|
<LeftSideComponent
|
||||||
<LeftSideTopSection active={isTorrentSourceActive}>
|
setIsUserInteractedWithPoster={setIsUserInteractedWithPoster}
|
||||||
<TextField
|
setSelectedFile={setSelectedFile}
|
||||||
onChange={handleTorrentSourceChange}
|
torrentSource={torrentSource}
|
||||||
value={torrentSource}
|
setTorrentSource={setTorrentSource}
|
||||||
margin='dense'
|
selectedFile={selectedFile}
|
||||||
label={t('TorrentSourceLink')}
|
/>
|
||||||
helperText={t('TorrentSourceOptions')}
|
|
||||||
type='text'
|
|
||||||
fullWidth
|
|
||||||
onFocus={() => setIsTorrentSourceActive(true)}
|
|
||||||
onBlur={() => setIsTorrentSourceActive(false)}
|
|
||||||
inputProps={{ autoComplete: 'off' }}
|
|
||||||
disabled={!!selectedFile}
|
|
||||||
/>
|
|
||||||
</LeftSideTopSection>
|
|
||||||
|
|
||||||
{selectedFile ? (
|
<RightSideComponent
|
||||||
<LeftSideBottomSectionFileSelected>
|
setTitle={setTitle}
|
||||||
<TorrentIconWrapper>
|
setPosterUrl={setPosterUrl}
|
||||||
<TorrentIcon />
|
setIsPosterUrlCorrect={setIsPosterUrlCorrect}
|
||||||
|
setIsUserInteractedWithPoster={setIsUserInteractedWithPoster}
|
||||||
<CancelIconWrapper onClick={clearSelectedFile}>
|
setPosterList={setPosterList}
|
||||||
<CancelIcon />
|
isTorrentSourceCorrect={isTorrentSourceCorrect}
|
||||||
</CancelIconWrapper>
|
title={title}
|
||||||
</TorrentIconWrapper>
|
posterUrl={posterUrl}
|
||||||
</LeftSideBottomSectionFileSelected>
|
isPosterUrlCorrect={isPosterUrlCorrect}
|
||||||
) : (
|
posterList={posterList}
|
||||||
<LeftSideBottomSectionNoFile isDragActive={isDragActive} {...getRootProps()}>
|
currentLang={currentLang}
|
||||||
<input {...getInputProps()} />
|
posterSearchLanguage={posterSearchLanguage}
|
||||||
<div>{t('AppendFile.Or')}</div>
|
setPosterSearchLanguage={setPosterSearchLanguage}
|
||||||
|
posterSearch={posterSearch}
|
||||||
<IconWrapper>
|
removePoster={removePoster}
|
||||||
<AddItemIcon color='primary' />
|
torrentSource={torrentSource}
|
||||||
<div>{t('AppendFile.ClickOrDrag')}</div>
|
/>
|
||||||
</IconWrapper>
|
|
||||||
</LeftSideBottomSectionNoFile>
|
|
||||||
)}
|
|
||||||
</LeftSide>
|
|
||||||
|
|
||||||
<RightSide>
|
|
||||||
<RightSideContainer isHidden={!isTorrentSourceCorrect}>
|
|
||||||
<TextField
|
|
||||||
onChange={handleTitleChange}
|
|
||||||
value={title}
|
|
||||||
margin='dense'
|
|
||||||
label={t('Title')}
|
|
||||||
type='text'
|
|
||||||
fullWidth
|
|
||||||
/>
|
|
||||||
<TextField
|
|
||||||
onChange={handlePosterUrlChange}
|
|
||||||
value={posterUrl}
|
|
||||||
margin='dense'
|
|
||||||
label={t('AddPosterLinkInput')}
|
|
||||||
type='url'
|
|
||||||
fullWidth
|
|
||||||
/>
|
|
||||||
|
|
||||||
<PosterWrapper>
|
|
||||||
<Poster poster={+isPosterUrlCorrect}>
|
|
||||||
{isPosterUrlCorrect ? <img src={posterUrl} alt='poster' /> : <NoImageIcon />}
|
|
||||||
</Poster>
|
|
||||||
|
|
||||||
<PosterSuggestions>
|
|
||||||
{posterList
|
|
||||||
?.filter(url => url !== posterUrl)
|
|
||||||
.slice(0, 12)
|
|
||||||
.map(url => (
|
|
||||||
<PosterSuggestionsItem onClick={() => userChangesPosterUrl(url)} key={uuidv4()}>
|
|
||||||
<img src={url} alt='poster' />
|
|
||||||
</PosterSuggestionsItem>
|
|
||||||
))}
|
|
||||||
</PosterSuggestions>
|
|
||||||
|
|
||||||
{currentLang !== 'en' && (
|
|
||||||
<PosterLanguageSwitch
|
|
||||||
onClick={() => {
|
|
||||||
const newLanguage = posterSearchLanguage === 'en' ? 'ru' : 'en'
|
|
||||||
setPosterSearchLanguage(newLanguage)
|
|
||||||
posterSearch(title, newLanguage, { shouldRefreshMainPoster: true })
|
|
||||||
}}
|
|
||||||
showbutton={+isPosterUrlCorrect}
|
|
||||||
color='primary'
|
|
||||||
variant='contained'
|
|
||||||
size='small'
|
|
||||||
>
|
|
||||||
{posterSearchLanguage === 'en' ? 'EN' : 'RU'}
|
|
||||||
</PosterLanguageSwitch>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<ClearPosterButton
|
|
||||||
showbutton={+isPosterUrlCorrect}
|
|
||||||
onClick={() => {
|
|
||||||
removePoster()
|
|
||||||
setIsUserInteractedWithPoster(true)
|
|
||||||
}}
|
|
||||||
color='primary'
|
|
||||||
variant='contained'
|
|
||||||
size='small'
|
|
||||||
>
|
|
||||||
{t('Clear')}
|
|
||||||
</ClearPosterButton>
|
|
||||||
</PosterWrapper>
|
|
||||||
</RightSideContainer>
|
|
||||||
|
|
||||||
<RightSideContainer
|
|
||||||
isError={torrentSource && !isTorrentSourceCorrect}
|
|
||||||
notificationMessage={
|
|
||||||
!torrentSource ? t('AddTorrentSourceNotification') : !isTorrentSourceCorrect && t('WrongTorrentSource')
|
|
||||||
}
|
|
||||||
isHidden={isTorrentSourceCorrect}
|
|
||||||
/>
|
|
||||||
</RightSide>
|
|
||||||
</Content>
|
</Content>
|
||||||
|
|
||||||
<ButtonWrapper>
|
<ButtonWrapper>
|
||||||
|
|||||||
87
web/src/components/Add/LeftSideComponent.jsx
Normal file
87
web/src/components/Add/LeftSideComponent.jsx
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { useDropzone } from 'react-dropzone'
|
||||||
|
import { AddItemIcon, TorrentIcon } from 'icons'
|
||||||
|
import TextField from '@material-ui/core/TextField'
|
||||||
|
import { Cancel as CancelIcon } from '@material-ui/icons'
|
||||||
|
import { useState } from 'react'
|
||||||
|
|
||||||
|
import {
|
||||||
|
CancelIconWrapper,
|
||||||
|
IconWrapper,
|
||||||
|
LeftSide,
|
||||||
|
LeftSideBottomSectionFileSelected,
|
||||||
|
LeftSideBottomSectionNoFile,
|
||||||
|
LeftSideTopSection,
|
||||||
|
TorrentIconWrapper,
|
||||||
|
} from './style'
|
||||||
|
|
||||||
|
export default function LeftSideComponent({
|
||||||
|
setIsUserInteractedWithPoster,
|
||||||
|
setSelectedFile,
|
||||||
|
torrentSource,
|
||||||
|
setTorrentSource,
|
||||||
|
selectedFile,
|
||||||
|
}) {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
|
||||||
|
const handleCapture = files => {
|
||||||
|
const [file] = files
|
||||||
|
if (!file) return
|
||||||
|
|
||||||
|
setIsUserInteractedWithPoster(false)
|
||||||
|
setSelectedFile(file)
|
||||||
|
setTorrentSource(file.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
const clearSelectedFile = () => {
|
||||||
|
setSelectedFile()
|
||||||
|
setTorrentSource('')
|
||||||
|
}
|
||||||
|
|
||||||
|
const [isTorrentSourceActive, setIsTorrentSourceActive] = useState(false)
|
||||||
|
const { getRootProps, getInputProps, isDragActive } = useDropzone({ onDrop: handleCapture, accept: '.torrent' })
|
||||||
|
|
||||||
|
const handleTorrentSourceChange = ({ target: { value } }) => setTorrentSource(value)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<LeftSide>
|
||||||
|
<LeftSideTopSection active={isTorrentSourceActive}>
|
||||||
|
<TextField
|
||||||
|
onChange={handleTorrentSourceChange}
|
||||||
|
value={torrentSource}
|
||||||
|
margin='dense'
|
||||||
|
label={t('TorrentSourceLink')}
|
||||||
|
helperText={t('TorrentSourceOptions')}
|
||||||
|
type='text'
|
||||||
|
fullWidth
|
||||||
|
onFocus={() => setIsTorrentSourceActive(true)}
|
||||||
|
onBlur={() => setIsTorrentSourceActive(false)}
|
||||||
|
inputProps={{ autoComplete: 'off' }}
|
||||||
|
disabled={!!selectedFile}
|
||||||
|
/>
|
||||||
|
</LeftSideTopSection>
|
||||||
|
|
||||||
|
{selectedFile ? (
|
||||||
|
<LeftSideBottomSectionFileSelected>
|
||||||
|
<TorrentIconWrapper>
|
||||||
|
<TorrentIcon />
|
||||||
|
|
||||||
|
<CancelIconWrapper onClick={clearSelectedFile}>
|
||||||
|
<CancelIcon />
|
||||||
|
</CancelIconWrapper>
|
||||||
|
</TorrentIconWrapper>
|
||||||
|
</LeftSideBottomSectionFileSelected>
|
||||||
|
) : (
|
||||||
|
<LeftSideBottomSectionNoFile isDragActive={isDragActive} {...getRootProps()}>
|
||||||
|
<input {...getInputProps()} />
|
||||||
|
<div>{t('AppendFile.Or')}</div>
|
||||||
|
|
||||||
|
<IconWrapper>
|
||||||
|
<AddItemIcon color='primary' />
|
||||||
|
<div>{t('AppendFile.ClickOrDrag')}</div>
|
||||||
|
</IconWrapper>
|
||||||
|
</LeftSideBottomSectionNoFile>
|
||||||
|
)}
|
||||||
|
</LeftSide>
|
||||||
|
)
|
||||||
|
}
|
||||||
120
web/src/components/Add/RightSideComponent.jsx
Normal file
120
web/src/components/Add/RightSideComponent.jsx
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { v4 as uuidv4 } from 'uuid'
|
||||||
|
import { NoImageIcon } from 'icons'
|
||||||
|
import { TextField } from '@material-ui/core'
|
||||||
|
|
||||||
|
import {
|
||||||
|
ClearPosterButton,
|
||||||
|
PosterLanguageSwitch,
|
||||||
|
RightSide,
|
||||||
|
Poster,
|
||||||
|
PosterSuggestions,
|
||||||
|
PosterSuggestionsItem,
|
||||||
|
PosterWrapper,
|
||||||
|
RightSideContainer,
|
||||||
|
} from './style'
|
||||||
|
import { checkImageURL } from './helpers'
|
||||||
|
|
||||||
|
export default function RightSideComponent({
|
||||||
|
setTitle,
|
||||||
|
setPosterUrl,
|
||||||
|
setIsPosterUrlCorrect,
|
||||||
|
setIsUserInteractedWithPoster,
|
||||||
|
setPosterList,
|
||||||
|
isTorrentSourceCorrect,
|
||||||
|
title,
|
||||||
|
posterUrl,
|
||||||
|
isPosterUrlCorrect,
|
||||||
|
posterList,
|
||||||
|
currentLang,
|
||||||
|
posterSearchLanguage,
|
||||||
|
setPosterSearchLanguage,
|
||||||
|
posterSearch,
|
||||||
|
removePoster,
|
||||||
|
torrentSource,
|
||||||
|
}) {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
|
||||||
|
const handleTitleChange = ({ target: { value } }) => setTitle(value)
|
||||||
|
const handlePosterUrlChange = ({ target: { value } }) => {
|
||||||
|
setPosterUrl(value)
|
||||||
|
checkImageURL(value).then(setIsPosterUrlCorrect)
|
||||||
|
setIsUserInteractedWithPoster(!!value)
|
||||||
|
setPosterList()
|
||||||
|
}
|
||||||
|
const userChangesPosterUrl = url => {
|
||||||
|
setPosterUrl(url)
|
||||||
|
checkImageURL(url).then(setIsPosterUrlCorrect)
|
||||||
|
setIsUserInteractedWithPoster(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<RightSide>
|
||||||
|
<RightSideContainer isHidden={!isTorrentSourceCorrect}>
|
||||||
|
<TextField onChange={handleTitleChange} value={title} margin='dense' label={t('Title')} type='text' fullWidth />
|
||||||
|
<TextField
|
||||||
|
onChange={handlePosterUrlChange}
|
||||||
|
value={posterUrl}
|
||||||
|
margin='dense'
|
||||||
|
label={t('AddPosterLinkInput')}
|
||||||
|
type='url'
|
||||||
|
fullWidth
|
||||||
|
/>
|
||||||
|
|
||||||
|
<PosterWrapper>
|
||||||
|
<Poster poster={+isPosterUrlCorrect}>
|
||||||
|
{isPosterUrlCorrect ? <img src={posterUrl} alt='poster' /> : <NoImageIcon />}
|
||||||
|
</Poster>
|
||||||
|
|
||||||
|
<PosterSuggestions>
|
||||||
|
{posterList
|
||||||
|
?.filter(url => url !== posterUrl)
|
||||||
|
.slice(0, 12)
|
||||||
|
.map(url => (
|
||||||
|
<PosterSuggestionsItem onClick={() => userChangesPosterUrl(url)} key={uuidv4()}>
|
||||||
|
<img src={url} alt='poster' />
|
||||||
|
</PosterSuggestionsItem>
|
||||||
|
))}
|
||||||
|
</PosterSuggestions>
|
||||||
|
|
||||||
|
{currentLang !== 'en' && (
|
||||||
|
<PosterLanguageSwitch
|
||||||
|
onClick={() => {
|
||||||
|
const newLanguage = posterSearchLanguage === 'en' ? 'ru' : 'en'
|
||||||
|
setPosterSearchLanguage(newLanguage)
|
||||||
|
posterSearch(title, newLanguage, { shouldRefreshMainPoster: true })
|
||||||
|
}}
|
||||||
|
showbutton={+isPosterUrlCorrect}
|
||||||
|
color='primary'
|
||||||
|
variant='contained'
|
||||||
|
size='small'
|
||||||
|
>
|
||||||
|
{posterSearchLanguage === 'en' ? 'EN' : 'RU'}
|
||||||
|
</PosterLanguageSwitch>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<ClearPosterButton
|
||||||
|
showbutton={+isPosterUrlCorrect}
|
||||||
|
onClick={() => {
|
||||||
|
removePoster()
|
||||||
|
setIsUserInteractedWithPoster(true)
|
||||||
|
}}
|
||||||
|
color='primary'
|
||||||
|
variant='contained'
|
||||||
|
size='small'
|
||||||
|
>
|
||||||
|
{t('Clear')}
|
||||||
|
</ClearPosterButton>
|
||||||
|
</PosterWrapper>
|
||||||
|
</RightSideContainer>
|
||||||
|
|
||||||
|
<RightSideContainer
|
||||||
|
isError={torrentSource && !isTorrentSourceCorrect}
|
||||||
|
notificationMessage={
|
||||||
|
!torrentSource ? t('AddTorrentSourceNotification') : !isTorrentSourceCorrect && t('WrongTorrentSource')
|
||||||
|
}
|
||||||
|
isHidden={isTorrentSourceCorrect}
|
||||||
|
/>
|
||||||
|
</RightSide>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,10 +1,19 @@
|
|||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
|
import parseTorrent from 'parse-torrent'
|
||||||
|
import ptt from 'parse-torrent-title'
|
||||||
|
|
||||||
export const getMoviePosters = (movieName, language = 'en') => {
|
export const getMoviePosters = (movieName, language = 'en') => {
|
||||||
const request = `${`http://api.themoviedb.org/3/search/multi?api_key=${process.env.REACT_APP_TMDB_API_KEY}`}&language=${language}&include_image_language=${language},null&query=${movieName}`
|
const url = 'http://api.themoviedb.org/3/search/multi'
|
||||||
|
|
||||||
return axios
|
return axios
|
||||||
.get(request)
|
.get(url, {
|
||||||
|
params: {
|
||||||
|
api_key: process.env.REACT_APP_TMDB_API_KEY,
|
||||||
|
language,
|
||||||
|
include_image_language: `${language},null`,
|
||||||
|
query: movieName,
|
||||||
|
},
|
||||||
|
})
|
||||||
.then(({ data: { results } }) =>
|
.then(({ data: { results } }) =>
|
||||||
results.filter(el => el.poster_path).map(el => `https://image.tmdb.org/t/p/w300${el.poster_path}`),
|
results.filter(el => el.poster_path).map(el => `https://image.tmdb.org/t/p/w300${el.poster_path}`),
|
||||||
)
|
)
|
||||||
@@ -27,3 +36,20 @@ const hashRegex = /^\b[0-9a-f]{32}\b$|^\b[0-9a-f]{40}\b$|^\b[0-9a-f]{64}\b$/i
|
|||||||
const torrentRegex = /^.*\.(torrent)$/i
|
const torrentRegex = /^.*\.(torrent)$/i
|
||||||
export const chechTorrentSource = source =>
|
export const chechTorrentSource = source =>
|
||||||
source.match(hashRegex) !== null || source.match(magnetRegex) !== null || source.match(torrentRegex) !== null
|
source.match(hashRegex) !== null || source.match(magnetRegex) !== null || source.match(torrentRegex) !== null
|
||||||
|
|
||||||
|
export const parseTorrentTitle = (parsingSource, callback) => {
|
||||||
|
parseTorrent.remote(parsingSource, (err, { name, files } = {}) => {
|
||||||
|
if (!name || err) return callback(null)
|
||||||
|
|
||||||
|
const torrentName = ptt.parse(name).title
|
||||||
|
const nameOfFileInsideTorrent = files ? ptt.parse(files[0].name).title : null
|
||||||
|
|
||||||
|
let newTitle = torrentName
|
||||||
|
if (nameOfFileInsideTorrent) {
|
||||||
|
// taking shorter title because in most cases it is more accurate
|
||||||
|
newTitle = torrentName.length < nameOfFileInsideTorrent.length ? torrentName : nameOfFileInsideTorrent
|
||||||
|
}
|
||||||
|
|
||||||
|
callback(newTitle)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
11
web/src/utils/usePreviousState.js
Executable file
11
web/src/utils/usePreviousState.js
Executable file
@@ -0,0 +1,11 @@
|
|||||||
|
import { useEffect, useRef } from 'react'
|
||||||
|
|
||||||
|
export default function usePreviousState(value) {
|
||||||
|
const ref = useRef(value)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
ref.current = value
|
||||||
|
}, [value])
|
||||||
|
|
||||||
|
return ref.current
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user