mirror of
https://github.com/Ernous/TorrServerJellyfin.git
synced 2025-12-19 21:46:11 +05:00
fix web
This commit is contained in:
@@ -24,7 +24,6 @@ func ParseFile(file multipart.File) (*torrent.TorrentSpec, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// mag := minfo.Magnet(info.Name, minfo.HashInfoBytes())
|
|
||||||
mag := minfo.Magnet(nil, &info)
|
mag := minfo.Magnet(nil, &info)
|
||||||
return &torrent.TorrentSpec{
|
return &torrent.TorrentSpec{
|
||||||
InfoBytes: minfo.InfoBytes,
|
InfoBytes: minfo.InfoBytes,
|
||||||
@@ -106,7 +105,6 @@ func fromHttp(link string) (*torrent.TorrentSpec, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// mag := minfo.Magnet(info.Name, minfo.HashInfoBytes())
|
|
||||||
mag := minfo.Magnet(nil, &info)
|
mag := minfo.Magnet(nil, &info)
|
||||||
|
|
||||||
return &torrent.TorrentSpec{
|
return &torrent.TorrentSpec{
|
||||||
@@ -130,7 +128,6 @@ func fromFile(path string) (*torrent.TorrentSpec, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// mag := minfo.Magnet(info.Name, minfo.HashInfoBytes())
|
|
||||||
mag := minfo.Magnet(nil, &info)
|
mag := minfo.Magnet(nil, &info)
|
||||||
return &torrent.TorrentSpec{
|
return &torrent.TorrentSpec{
|
||||||
InfoBytes: minfo.InfoBytes,
|
InfoBytes: minfo.InfoBytes,
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ func getTorrents(c *gin.Context) {
|
|||||||
mi := metainfo.MetaInfo{
|
mi := metainfo.MetaInfo{
|
||||||
AnnounceList: ts.Trackers,
|
AnnounceList: ts.Trackers,
|
||||||
}
|
}
|
||||||
// mag := mi.Magnet(ts.DisplayName, ts.InfoHash)
|
|
||||||
mag := mi.Magnet(&ts.InfoHash, &metainfo.Info{Name: ts.DisplayName})
|
mag := mi.Magnet(&ts.InfoHash, &metainfo.Info{Name: ts.DisplayName})
|
||||||
http += "<p><a href='" + mag.String() + "'>magnet:?xt=urn:btih:" + mag.InfoHash.HexString() + "</a></p>"
|
http += "<p><a href='" + mag.String() + "'>magnet:?xt=urn:btih:" + mag.InfoHash.HexString() + "</a></p>"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,51 @@ func RouteWebPages(route *gin.RouterGroup) {
|
|||||||
c.Data(200, "text/html; charset=utf-8", Indexhtml)
|
c.Data(200, "text/html; charset=utf-8", Indexhtml)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
route.GET("/favicon-16x16.png", func(c *gin.Context) {
|
||||||
|
c.Data(200, "image/png", Favicon16x16png)
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
route.GET("/static/js/main.2dd9f580.chunk.js.map", func(c *gin.Context) {
|
||||||
|
c.Data(200, "application/json", Staticjsmain2dd9f580chunkjsmap)
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
route.GET("/android-chrome-512x512.png", func(c *gin.Context) {
|
||||||
|
c.Data(200, "image/png", Androidchrome512x512png)
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
route.GET("/apple-touch-icon.png", func(c *gin.Context) {
|
||||||
|
c.Data(200, "image/png", Appletouchiconpng)
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
route.GET("/index.html", func(c *gin.Context) {
|
||||||
|
c.Data(200, "text/html; charset=utf-8", Indexhtml)
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
route.GET("/static/js/main.2dd9f580.chunk.js", func(c *gin.Context) {
|
||||||
|
c.Data(200, "application/javascript", Staticjsmain2dd9f580chunkjs)
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
route.GET("/static/js/runtime-main.8bda5920.js", func(c *gin.Context) {
|
||||||
|
c.Data(200, "application/javascript", Staticjsruntimemain8bda5920js)
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
route.GET("/static/js/runtime-main.8bda5920.js.map", func(c *gin.Context) {
|
||||||
|
c.Data(200, "application/json", Staticjsruntimemain8bda5920jsmap)
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
route.GET("/android-chrome-192x192.png", func(c *gin.Context) {
|
||||||
|
c.Data(200, "image/png", Androidchrome192x192png)
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
route.GET("/asset-manifest.json", func(c *gin.Context) {
|
route.GET("/asset-manifest.json", func(c *gin.Context) {
|
||||||
c.Data(200, "application/json", Assetmanifestjson)
|
c.Data(200, "application/json", Assetmanifestjson)
|
||||||
})
|
})
|
||||||
@@ -19,71 +64,11 @@ func RouteWebPages(route *gin.RouterGroup) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
route.GET("/mstile-150x150.png", func(c *gin.Context) {
|
|
||||||
c.Data(200, "image/png", Mstile150x150png)
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
route.GET("/static/js/2.9044ecaa.chunk.js", func(c *gin.Context) {
|
|
||||||
c.Data(200, "application/javascript", Staticjs29044ecaachunkjs)
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
route.GET("/android-chrome-512x512.png", func(c *gin.Context) {
|
|
||||||
c.Data(200, "image/png", Androidchrome512x512png)
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
route.GET("/favicon-16x16.png", func(c *gin.Context) {
|
|
||||||
c.Data(200, "image/png", Favicon16x16png)
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
route.GET("/index.html", func(c *gin.Context) {
|
|
||||||
c.Data(200, "text/html; charset=utf-8", Indexhtml)
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
route.GET("/static/js/runtime-main.8bda5920.js", func(c *gin.Context) {
|
|
||||||
c.Data(200, "application/javascript", Staticjsruntimemain8bda5920js)
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
route.GET("/android-chrome-192x192.png", func(c *gin.Context) {
|
|
||||||
c.Data(200, "image/png", Androidchrome192x192png)
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
route.GET("/favicon-32x32.png", func(c *gin.Context) {
|
route.GET("/favicon-32x32.png", func(c *gin.Context) {
|
||||||
c.Data(200, "image/png", Favicon32x32png)
|
c.Data(200, "image/png", Favicon32x32png)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
route.GET("/favicon.ico", func(c *gin.Context) {
|
|
||||||
c.Data(200, "image/x-icon", Faviconico)
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
route.GET("/static/js/main.2dd9f580.chunk.js", func(c *gin.Context) {
|
|
||||||
c.Data(200, "application/javascript", Staticjsmain2dd9f580chunkjs)
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
route.GET("/static/js/runtime-main.8bda5920.js.map", func(c *gin.Context) {
|
|
||||||
c.Data(200, "application/json", Staticjsruntimemain8bda5920jsmap)
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
route.GET("/apple-touch-icon.png", func(c *gin.Context) {
|
|
||||||
c.Data(200, "image/png", Appletouchiconpng)
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
route.GET("/site.webmanifest", func(c *gin.Context) {
|
|
||||||
c.Data(200, "application/manifest+json", Sitewebmanifest)
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
route.GET("/static/js/2.9044ecaa.chunk.js.LICENSE.txt", func(c *gin.Context) {
|
route.GET("/static/js/2.9044ecaa.chunk.js.LICENSE.txt", func(c *gin.Context) {
|
||||||
c.Data(200, "text/plain; charset=utf-8", Staticjs29044ecaachunkjsLICENSEtxt)
|
c.Data(200, "text/plain; charset=utf-8", Staticjs29044ecaachunkjsLICENSEtxt)
|
||||||
})
|
})
|
||||||
@@ -94,8 +79,23 @@ func RouteWebPages(route *gin.RouterGroup) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
route.GET("/static/js/main.2dd9f580.chunk.js.map", func(c *gin.Context) {
|
route.GET("/favicon.ico", func(c *gin.Context) {
|
||||||
c.Data(200, "application/json", Staticjsmain2dd9f580chunkjsmap)
|
c.Data(200, "image/x-icon", Faviconico)
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
route.GET("/mstile-150x150.png", func(c *gin.Context) {
|
||||||
|
c.Data(200, "image/png", Mstile150x150png)
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
route.GET("/site.webmanifest", func(c *gin.Context) {
|
||||||
|
c.Data(200, "application/manifest+json", Sitewebmanifest)
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
route.GET("/static/js/2.9044ecaa.chunk.js", func(c *gin.Context) {
|
||||||
|
c.Data(200, "application/javascript", Staticjs29044ecaachunkjs)
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1 +0,0 @@
|
|||||||
REACT_APP_SERVER_HOST=http://127.0.0.1:8090
|
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
import { playlistAllHost } from 'utils/Hosts'
|
|
||||||
import Divider from '@material-ui/core/Divider'
|
|
||||||
import ListItem from '@material-ui/core/ListItem'
|
|
||||||
import ListItemIcon from '@material-ui/core/ListItemIcon'
|
|
||||||
import ListItemText from '@material-ui/core/ListItemText'
|
|
||||||
import AddDialogButton from 'components/Add'
|
|
||||||
import RemoveAll from 'components/RemoveAll'
|
|
||||||
import SettingsDialog from 'components/Settings'
|
|
||||||
import AboutDialog from 'components/About'
|
|
||||||
import UploadDialog from 'components/Upload'
|
|
||||||
import { CreditCard as CreditCardIcon, List as ListIcon, Language as LanguageIcon } from '@material-ui/icons'
|
|
||||||
import List from '@material-ui/core/List'
|
|
||||||
import CloseServer from 'components/CloseServer'
|
|
||||||
import { useTranslation } from 'react-i18next'
|
|
||||||
import useChangeLanguage from 'utils/useChangeLanguage'
|
|
||||||
|
|
||||||
import { AppSidebarStyle } from './style'
|
|
||||||
|
|
||||||
export default function Sidebar({ isDrawerOpen, setIsDonationDialogOpen }) {
|
|
||||||
const [currentLang, changeLang] = useChangeLanguage()
|
|
||||||
const { t } = useTranslation()
|
|
||||||
|
|
||||||
return (
|
|
||||||
<AppSidebarStyle isDrawerOpen={isDrawerOpen}>
|
|
||||||
<List>
|
|
||||||
<AddDialogButton />
|
|
||||||
<UploadDialog />
|
|
||||||
<RemoveAll />
|
|
||||||
<ListItem button component='a' target='_blank' href={playlistAllHost()}>
|
|
||||||
<ListItemIcon>
|
|
||||||
<ListIcon />
|
|
||||||
</ListItemIcon>
|
|
||||||
<ListItemText primary={t('PlaylistAll')} />
|
|
||||||
</ListItem>
|
|
||||||
</List>
|
|
||||||
|
|
||||||
<Divider />
|
|
||||||
|
|
||||||
<List>
|
|
||||||
<SettingsDialog />
|
|
||||||
|
|
||||||
<ListItem button onClick={() => (currentLang === 'en' ? changeLang('ru') : changeLang('en'))}>
|
|
||||||
<ListItemIcon>
|
|
||||||
<LanguageIcon />
|
|
||||||
</ListItemIcon>
|
|
||||||
<ListItemText primary={t('ChooseLanguage')} />
|
|
||||||
</ListItem>
|
|
||||||
|
|
||||||
<AboutDialog />
|
|
||||||
<CloseServer />
|
|
||||||
</List>
|
|
||||||
|
|
||||||
<Divider />
|
|
||||||
|
|
||||||
<List>
|
|
||||||
<ListItem button onClick={() => setIsDonationDialogOpen(true)}>
|
|
||||||
<ListItemIcon>
|
|
||||||
<CreditCardIcon />
|
|
||||||
</ListItemIcon>
|
|
||||||
<ListItemText primary={t('Donate')} />
|
|
||||||
</ListItem>
|
|
||||||
</List>
|
|
||||||
</AppSidebarStyle>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
import CssBaseline from '@material-ui/core/CssBaseline'
|
|
||||||
import { createMuiTheme, MuiThemeProvider } from '@material-ui/core'
|
|
||||||
import { useEffect, useState } from 'react'
|
|
||||||
import Typography from '@material-ui/core/Typography'
|
|
||||||
import IconButton from '@material-ui/core/IconButton'
|
|
||||||
import { Menu as MenuIcon, Close as CloseIcon } from '@material-ui/icons'
|
|
||||||
import { echoHost } from 'utils/Hosts'
|
|
||||||
import TorrentList from 'components/TorrentList'
|
|
||||||
import DonateSnackbar from 'components/Donate'
|
|
||||||
import DonateDialog from 'components/Donate/DonateDialog'
|
|
||||||
import Div100vh from 'react-div-100vh'
|
|
||||||
import axios from 'axios'
|
|
||||||
|
|
||||||
import { AppWrapper, AppHeader } from './style'
|
|
||||||
import Sidebar from './Sidebar'
|
|
||||||
|
|
||||||
const baseTheme = createMuiTheme({
|
|
||||||
overrides: { MuiCssBaseline: { '@global': { html: { WebkitFontSmoothing: 'auto' } } } },
|
|
||||||
palette: { primary: { main: '#00a572' }, secondary: { main: '#ffa724' }, tonalOffset: 0.2 },
|
|
||||||
})
|
|
||||||
|
|
||||||
export default function App() {
|
|
||||||
const [isDrawerOpen, setIsDrawerOpen] = useState(false)
|
|
||||||
const [isDonationDialogOpen, setIsDonationDialogOpen] = useState(false)
|
|
||||||
const [torrServerVersion, setTorrServerVersion] = useState('')
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
axios.get(echoHost()).then(({ data }) => setTorrServerVersion(data))
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
return (
|
|
||||||
<MuiThemeProvider theme={baseTheme}>
|
|
||||||
<CssBaseline />
|
|
||||||
|
|
||||||
{/* Div100vh - iOS WebKit fix */}
|
|
||||||
<Div100vh>
|
|
||||||
<AppWrapper>
|
|
||||||
<AppHeader>
|
|
||||||
<IconButton
|
|
||||||
style={{ marginRight: '20px' }}
|
|
||||||
color='inherit'
|
|
||||||
onClick={() => setIsDrawerOpen(!isDrawerOpen)}
|
|
||||||
edge='start'
|
|
||||||
>
|
|
||||||
{isDrawerOpen ? <CloseIcon /> : <MenuIcon />}
|
|
||||||
</IconButton>
|
|
||||||
|
|
||||||
<Typography variant='h6' noWrap>
|
|
||||||
TorrServer {torrServerVersion}
|
|
||||||
</Typography>
|
|
||||||
</AppHeader>
|
|
||||||
|
|
||||||
<Sidebar isDrawerOpen={isDrawerOpen} setIsDonationDialogOpen={setIsDonationDialogOpen} />
|
|
||||||
|
|
||||||
<TorrentList />
|
|
||||||
|
|
||||||
{isDonationDialogOpen && <DonateDialog onClose={() => setIsDonationDialogOpen(false)} />}
|
|
||||||
{!JSON.parse(localStorage.getItem('snackbarIsClosed')) && <DonateSnackbar />}
|
|
||||||
</AppWrapper>
|
|
||||||
</Div100vh>
|
|
||||||
</MuiThemeProvider>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
import styled, { css } from 'styled-components'
|
|
||||||
|
|
||||||
export const AppWrapper = styled.div`
|
|
||||||
height: 100%;
|
|
||||||
background: #cbe8d9;
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: 60px 1fr;
|
|
||||||
grid-template-rows: 60px 1fr;
|
|
||||||
grid-template-areas:
|
|
||||||
'head head'
|
|
||||||
'side content';
|
|
||||||
`
|
|
||||||
|
|
||||||
export const CenteredGrid = styled.div`
|
|
||||||
height: 100%;
|
|
||||||
display: grid;
|
|
||||||
place-items: center;
|
|
||||||
`
|
|
||||||
|
|
||||||
export const AppHeader = styled.div`
|
|
||||||
background: #00a572;
|
|
||||||
color: rgba(0, 0, 0, 0.87);
|
|
||||||
grid-area: head;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
box-shadow: 0px 2px 4px -1px rgb(0 0 0 / 20%), 0px 4px 5px 0px rgb(0 0 0 / 14%), 0px 1px 10px 0px rgb(0 0 0 / 12%);
|
|
||||||
padding: 0 24px;
|
|
||||||
z-index: 3;
|
|
||||||
`
|
|
||||||
export const AppSidebarStyle = styled.div`
|
|
||||||
${({ isDrawerOpen }) => css`
|
|
||||||
grid-area: side;
|
|
||||||
width: ${isDrawerOpen ? '400%' : '100%'};
|
|
||||||
z-index: 2;
|
|
||||||
overflow-x: hidden;
|
|
||||||
transition: width 195ms cubic-bezier(0.4, 0, 0.6, 1) 0ms;
|
|
||||||
border-right: 1px solid rgba(0, 0, 0, 0.12);
|
|
||||||
background: #eee;
|
|
||||||
white-space: nowrap;
|
|
||||||
`}
|
|
||||||
`
|
|
||||||
export const TorrentListWrapper = styled.div`
|
|
||||||
grid-area: content;
|
|
||||||
padding: 20px;
|
|
||||||
overflow: auto;
|
|
||||||
|
|
||||||
display: grid;
|
|
||||||
place-content: start;
|
|
||||||
grid-template-columns: repeat(auto-fit, minmax(max-content, 570px));
|
|
||||||
gap: 20px;
|
|
||||||
|
|
||||||
@media (max-width: 1260px), (max-height: 500px) {
|
|
||||||
padding: 10px;
|
|
||||||
gap: 15px;
|
|
||||||
grid-template-columns: repeat(3, 1fr);
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 1100px) {
|
|
||||||
grid-template-columns: repeat(2, 1fr);
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 700px) {
|
|
||||||
grid-template-columns: 1fr;
|
|
||||||
}
|
|
||||||
`
|
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
import axios from 'axios'
|
|
||||||
import { useEffect, useState } from 'react'
|
|
||||||
import Button from '@material-ui/core/Button'
|
|
||||||
import Dialog from '@material-ui/core/Dialog'
|
|
||||||
import DialogActions from '@material-ui/core/DialogActions'
|
|
||||||
import DialogContent from '@material-ui/core/DialogContent'
|
|
||||||
import DialogTitle from '@material-ui/core/DialogTitle'
|
|
||||||
import InfoIcon from '@material-ui/icons/Info'
|
|
||||||
import ListItem from '@material-ui/core/ListItem'
|
|
||||||
import ListItemIcon from '@material-ui/core/ListItemIcon'
|
|
||||||
import ListItemText from '@material-ui/core/ListItemText'
|
|
||||||
import { useTranslation } from 'react-i18next'
|
|
||||||
import { echoHost } from 'utils/Hosts'
|
|
||||||
|
|
||||||
export default function AboutDialog() {
|
|
||||||
const { t } = useTranslation()
|
|
||||||
const [open, setOpen] = useState(false)
|
|
||||||
const [torrServerVersion, setTorrServerVersion] = useState('')
|
|
||||||
useEffect(() => {
|
|
||||||
axios.get(echoHost()).then(({ data }) => setTorrServerVersion(data))
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<ListItem button key='Settings' onClick={() => setOpen(true)}>
|
|
||||||
<ListItemIcon>
|
|
||||||
<InfoIcon />
|
|
||||||
</ListItemIcon>
|
|
||||||
<ListItemText primary={t('About')} />
|
|
||||||
</ListItem>
|
|
||||||
|
|
||||||
<Dialog open={open} onClose={() => setOpen(false)} aria-labelledby='form-dialog-title' fullWidth maxWidth='lg'>
|
|
||||||
<DialogTitle id='form-dialog-title'>{t('About')}</DialogTitle>
|
|
||||||
|
|
||||||
<DialogContent>
|
|
||||||
<center>
|
|
||||||
<h2>TorrServer {torrServerVersion}</h2>
|
|
||||||
<a href='https://github.com/YouROK/TorrServer'>https://github.com/YouROK/TorrServer</a>
|
|
||||||
</center>
|
|
||||||
<DialogContent>
|
|
||||||
<center>
|
|
||||||
<h2>{t('ThanksToEveryone')}</h2>
|
|
||||||
</center>
|
|
||||||
<br />
|
|
||||||
<h2>{t('SpecialThanks')}</h2>
|
|
||||||
<b>anacrolix Matt Joiner</b> <a href='https://github.com/anacrolix/'>github.com/anacrolix</a>
|
|
||||||
<br />
|
|
||||||
<b>nikk</b> <a href='https://github.com/tsynik'>github.com/tsynik</a>
|
|
||||||
<br />
|
|
||||||
<b>dancheskus</b> <a href='https://github.com/dancheskus'>github.com/dancheskus</a>
|
|
||||||
<br />
|
|
||||||
<b>tw1cker Руслан Пахнев</b> <a href='https://github.com/Nemiroff'>github.com/Nemiroff</a>
|
|
||||||
<br />
|
|
||||||
<b>SpAwN_LMG</b>
|
|
||||||
<br />
|
|
||||||
</DialogContent>
|
|
||||||
</DialogContent>
|
|
||||||
|
|
||||||
<DialogActions>
|
|
||||||
<Button onClick={() => setOpen(false)} color='primary' variant='outlined' autoFocus>
|
|
||||||
{t('Close')}
|
|
||||||
</Button>
|
|
||||||
</DialogActions>
|
|
||||||
</Dialog>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,125 +0,0 @@
|
|||||||
import { useEffect, useState } from 'react'
|
|
||||||
import DialogContent from '@material-ui/core/DialogContent'
|
|
||||||
import { Stage, Layer } from 'react-konva'
|
|
||||||
import Measure from 'react-measure'
|
|
||||||
import { v4 as uuidv4 } from 'uuid'
|
|
||||||
import styled from 'styled-components'
|
|
||||||
|
|
||||||
import SingleBlock from './SingleBlock'
|
|
||||||
import getShortCacheMap from './getShortCacheMap'
|
|
||||||
|
|
||||||
const ScrollNotification = styled.div`
|
|
||||||
margin-top: 10px;
|
|
||||||
text-transform: uppercase;
|
|
||||||
color: rgba(0, 0, 0, 0.5);
|
|
||||||
align-self: center;
|
|
||||||
`
|
|
||||||
|
|
||||||
export default function DefaultSnake({ isMini, cacheMap, preloadPiecesAmount }) {
|
|
||||||
const [dimensions, setDimensions] = useState({ width: 0, height: 0 })
|
|
||||||
const [stageSettings, setStageSettings] = useState({
|
|
||||||
boxHeight: null,
|
|
||||||
strokeWidth: null,
|
|
||||||
marginBetweenBlocks: null,
|
|
||||||
stageOffset: null,
|
|
||||||
})
|
|
||||||
const updateStageSettings = (boxHeight, strokeWidth) => {
|
|
||||||
setStageSettings({
|
|
||||||
boxHeight,
|
|
||||||
strokeWidth,
|
|
||||||
marginBetweenBlocks: strokeWidth,
|
|
||||||
stageOffset: strokeWidth * 2,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
// initializing stageSettings
|
|
||||||
if (isMini) return dimensions.width < 500 ? updateStageSettings(20, 3) : updateStageSettings(24, 4)
|
|
||||||
updateStageSettings(12, 2)
|
|
||||||
}, [isMini, dimensions.width])
|
|
||||||
|
|
||||||
const miniCacheMaxHeight = 340
|
|
||||||
|
|
||||||
const { boxHeight, strokeWidth, marginBetweenBlocks, stageOffset } = stageSettings
|
|
||||||
|
|
||||||
const blockSizeWithMargin = boxHeight + strokeWidth + marginBetweenBlocks
|
|
||||||
const piecesInOneRow = Math.floor((dimensions.width * 0.9) / blockSizeWithMargin)
|
|
||||||
|
|
||||||
const shortCacheMap = isMini ? getShortCacheMap({ cacheMap, preloadPiecesAmount, piecesInOneRow }) : []
|
|
||||||
|
|
||||||
const amountOfRows = Math.ceil((isMini ? shortCacheMap.length : cacheMap.length) / piecesInOneRow)
|
|
||||||
|
|
||||||
const getItemCoordinates = blockOrder => {
|
|
||||||
const currentRow = Math.floor(blockOrder / piecesInOneRow)
|
|
||||||
const x = (blockOrder % piecesInOneRow) * blockSizeWithMargin || 0
|
|
||||||
const y = currentRow * blockSizeWithMargin || 0
|
|
||||||
|
|
||||||
return { x, y }
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Measure bounds onResize={({ bounds }) => setDimensions(bounds)}>
|
|
||||||
{({ measureRef }) => (
|
|
||||||
<div style={{ display: 'flex', flexDirection: 'column' }}>
|
|
||||||
<DialogContent
|
|
||||||
ref={measureRef}
|
|
||||||
{...(isMini
|
|
||||||
? { style: { padding: 0, maxHeight: `${miniCacheMaxHeight}px`, overflow: 'auto' } }
|
|
||||||
: { style: { padding: 0 } })}
|
|
||||||
>
|
|
||||||
<Stage
|
|
||||||
style={{ display: 'flex', justifyContent: 'center' }}
|
|
||||||
offset={{ x: -stageOffset, y: -stageOffset }}
|
|
||||||
width={stageOffset + blockSizeWithMargin * piecesInOneRow || 0}
|
|
||||||
height={stageOffset + blockSizeWithMargin * amountOfRows || 0}
|
|
||||||
>
|
|
||||||
<Layer>
|
|
||||||
{isMini
|
|
||||||
? shortCacheMap.map(({ percentage, isComplete, inProgress, isActive, isReaderRange }, i) => {
|
|
||||||
const { x, y } = getItemCoordinates(i)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<SingleBlock
|
|
||||||
key={uuidv4()}
|
|
||||||
x={x}
|
|
||||||
y={y}
|
|
||||||
percentage={percentage}
|
|
||||||
inProgress={inProgress}
|
|
||||||
isComplete={isComplete}
|
|
||||||
isReaderRange={isReaderRange}
|
|
||||||
isActive={isActive}
|
|
||||||
boxHeight={boxHeight}
|
|
||||||
strokeWidth={strokeWidth}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
})
|
|
||||||
: cacheMap.map(({ id, percentage, isComplete, inProgress, isActive, isReaderRange }) => {
|
|
||||||
const { x, y } = getItemCoordinates(id)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<SingleBlock
|
|
||||||
key={uuidv4()}
|
|
||||||
x={x}
|
|
||||||
y={y}
|
|
||||||
percentage={percentage}
|
|
||||||
inProgress={inProgress}
|
|
||||||
isComplete={isComplete}
|
|
||||||
isReaderRange={isReaderRange}
|
|
||||||
isActive={isActive}
|
|
||||||
boxHeight={boxHeight}
|
|
||||||
strokeWidth={strokeWidth}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
</Layer>
|
|
||||||
</Stage>
|
|
||||||
</DialogContent>
|
|
||||||
|
|
||||||
{isMini &&
|
|
||||||
(stageOffset + blockSizeWithMargin * amountOfRows || 0) >= miniCacheMaxHeight &&
|
|
||||||
dimensions.height >= miniCacheMaxHeight && <ScrollNotification>scroll down</ScrollNotification>}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</Measure>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
import { FixedSizeGrid as Grid } from 'react-window'
|
|
||||||
import AutoSizer from 'react-virtualized-auto-sizer'
|
|
||||||
|
|
||||||
import { getLargeSnakeColors } from './colors'
|
|
||||||
|
|
||||||
const Cell = ({ columnIndex, rowIndex, style, data }) => {
|
|
||||||
const { columnCount, cacheMap, gutterSize, borderSize, pieces } = data
|
|
||||||
const itemIndex = rowIndex * columnCount + columnIndex
|
|
||||||
|
|
||||||
const { borderColor, backgroundColor } = getLargeSnakeColors(cacheMap[itemIndex] || {})
|
|
||||||
|
|
||||||
const newStyle = {
|
|
||||||
...style,
|
|
||||||
left: style.left + gutterSize,
|
|
||||||
top: style.top + gutterSize,
|
|
||||||
width: style.width - gutterSize,
|
|
||||||
height: style.height - gutterSize,
|
|
||||||
border: `${borderSize}px solid ${borderColor}`,
|
|
||||||
display: itemIndex >= pieces ? 'none' : null,
|
|
||||||
background: backgroundColor,
|
|
||||||
}
|
|
||||||
|
|
||||||
return <div style={newStyle} />
|
|
||||||
}
|
|
||||||
|
|
||||||
const gutterSize = 2
|
|
||||||
const borderSize = 1
|
|
||||||
const pieceSize = 12
|
|
||||||
const pieceSizeWithSpacing = pieceSize + gutterSize
|
|
||||||
|
|
||||||
export default function LargeSnake({ cacheMap }) {
|
|
||||||
const pieces = cacheMap.length
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div style={{ height: '60vh', overflow: 'hidden' }}>
|
|
||||||
<AutoSizer>
|
|
||||||
{({ height, width }) => {
|
|
||||||
const columnCount = Math.floor(width / (gutterSize + pieceSize)) - 1
|
|
||||||
const rowCount = pieces / columnCount + 1
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Grid
|
|
||||||
columnCount={columnCount}
|
|
||||||
rowCount={rowCount}
|
|
||||||
columnWidth={pieceSizeWithSpacing}
|
|
||||||
rowHeight={pieceSizeWithSpacing}
|
|
||||||
height={height}
|
|
||||||
width={width}
|
|
||||||
itemData={{ columnCount, cacheMap, gutterSize, borderSize, pieces }}
|
|
||||||
>
|
|
||||||
{Cell}
|
|
||||||
</Grid>
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
</AutoSizer>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
import { Rect } from 'react-konva'
|
|
||||||
|
|
||||||
import { activeColor, completeColor, defaultBorderColor, progressColor, rangeColor } from './colors'
|
|
||||||
|
|
||||||
export default function SingleBlock({
|
|
||||||
x,
|
|
||||||
y,
|
|
||||||
percentage,
|
|
||||||
isActive = false,
|
|
||||||
inProgress = false,
|
|
||||||
isReaderRange = false,
|
|
||||||
isComplete = false,
|
|
||||||
boxHeight,
|
|
||||||
strokeWidth,
|
|
||||||
}) {
|
|
||||||
const strokeColor = isActive
|
|
||||||
? activeColor
|
|
||||||
: isComplete
|
|
||||||
? completeColor
|
|
||||||
: inProgress
|
|
||||||
? progressColor
|
|
||||||
: isReaderRange
|
|
||||||
? rangeColor
|
|
||||||
: defaultBorderColor
|
|
||||||
const backgroundColor = inProgress ? progressColor : defaultBorderColor
|
|
||||||
const percentageProgressColor = completeColor
|
|
||||||
const processCompletedColor = completeColor
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Rect
|
|
||||||
x={x}
|
|
||||||
y={y}
|
|
||||||
stroke={strokeColor}
|
|
||||||
strokeWidth={strokeWidth}
|
|
||||||
height={boxHeight}
|
|
||||||
width={boxHeight}
|
|
||||||
fillAfterStrokeEnabled
|
|
||||||
preventDefault={false}
|
|
||||||
{...(isComplete
|
|
||||||
? { fill: processCompletedColor }
|
|
||||||
: inProgress && {
|
|
||||||
fillLinearGradientStartPointY: boxHeight,
|
|
||||||
fillLinearGradientEndPointY: 0,
|
|
||||||
fillLinearGradientColorStops: [
|
|
||||||
0,
|
|
||||||
percentageProgressColor,
|
|
||||||
percentage,
|
|
||||||
percentageProgressColor,
|
|
||||||
percentage,
|
|
||||||
backgroundColor,
|
|
||||||
],
|
|
||||||
})}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
export const defaultBorderColor = '#eef2f4'
|
|
||||||
export const defaultBackgroundColor = '#fff'
|
|
||||||
export const completeColor = '#00a572'
|
|
||||||
export const progressColor = '#ffa724'
|
|
||||||
export const activeColor = '#000'
|
|
||||||
export const rangeColor = '#9a9aff'
|
|
||||||
|
|
||||||
export const getLargeSnakeColors = ({ isActive, isComplete, inProgress, isReaderRange, percentage }) => {
|
|
||||||
const gradientBackgroundColor = inProgress ? progressColor : defaultBackgroundColor
|
|
||||||
const gradient = `linear-gradient(to top, ${completeColor} 0%, ${completeColor} ${
|
|
||||||
percentage * 100
|
|
||||||
}%, ${gradientBackgroundColor} ${percentage * 100}%, ${gradientBackgroundColor} 100%)`
|
|
||||||
|
|
||||||
const borderColor = isActive
|
|
||||||
? activeColor
|
|
||||||
: isComplete
|
|
||||||
? completeColor
|
|
||||||
: inProgress
|
|
||||||
? progressColor
|
|
||||||
: isReaderRange
|
|
||||||
? rangeColor
|
|
||||||
: defaultBorderColor
|
|
||||||
const backgroundColor = isComplete ? completeColor : inProgress ? gradient : defaultBackgroundColor
|
|
||||||
|
|
||||||
return { borderColor, backgroundColor }
|
|
||||||
}
|
|
||||||
@@ -1,101 +0,0 @@
|
|||||||
import {
|
|
||||||
ArrowDownward as ArrowDownwardIcon,
|
|
||||||
ArrowUpward as ArrowUpwardIcon,
|
|
||||||
SwapVerticalCircle as SwapVerticalCircleIcon,
|
|
||||||
ViewAgenda as ViewAgendaIcon,
|
|
||||||
Widgets as WidgetsIcon,
|
|
||||||
PhotoSizeSelectSmall as PhotoSizeSelectSmallIcon,
|
|
||||||
Build as BuildIcon,
|
|
||||||
} from '@material-ui/icons'
|
|
||||||
import { getPeerString, humanizeSize } from 'utils/Utils'
|
|
||||||
import { useTranslation } from 'react-i18next'
|
|
||||||
|
|
||||||
import StatisticsField from './StatisticsField'
|
|
||||||
|
|
||||||
export const DownlodSpeedWidget = ({ data }) => {
|
|
||||||
const { t } = useTranslation()
|
|
||||||
return (
|
|
||||||
<StatisticsField
|
|
||||||
title={t('DownloadSpeed')}
|
|
||||||
value={humanizeSize(data) || '0 B'}
|
|
||||||
iconBg='#118f00'
|
|
||||||
valueBg='#13a300'
|
|
||||||
icon={ArrowDownwardIcon}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export const UploadSpeedWidget = ({ data }) => {
|
|
||||||
const { t } = useTranslation()
|
|
||||||
return (
|
|
||||||
<StatisticsField
|
|
||||||
title={t('UploadSpeed')}
|
|
||||||
value={humanizeSize(data) || '0 B'}
|
|
||||||
iconBg='#0146ad'
|
|
||||||
valueBg='#0058db'
|
|
||||||
icon={ArrowUpwardIcon}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export const PeersWidget = ({ data }) => {
|
|
||||||
const { t } = useTranslation()
|
|
||||||
return (
|
|
||||||
<StatisticsField
|
|
||||||
title={t('Peers')}
|
|
||||||
value={getPeerString(data) || '[0] 0 / 0'}
|
|
||||||
iconBg='#cdc118'
|
|
||||||
valueBg='#d8cb18'
|
|
||||||
icon={SwapVerticalCircleIcon}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export const PiecesCountWidget = ({ data }) => {
|
|
||||||
const { t } = useTranslation()
|
|
||||||
return <StatisticsField title={t('PiecesCount')} value={data} iconBg='#b6c95e' valueBg='#c0d076' icon={WidgetsIcon} />
|
|
||||||
}
|
|
||||||
|
|
||||||
export const PiecesLengthWidget = ({ data }) => {
|
|
||||||
const { t } = useTranslation()
|
|
||||||
return (
|
|
||||||
<StatisticsField
|
|
||||||
title={t('PiecesLength')}
|
|
||||||
value={humanizeSize(data)}
|
|
||||||
iconBg='#0982c8'
|
|
||||||
valueBg='#098cd7'
|
|
||||||
icon={PhotoSizeSelectSmallIcon}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export const StatusWidget = ({ data }) => {
|
|
||||||
const { t } = useTranslation()
|
|
||||||
let i18nd = data
|
|
||||||
if (data.toLowerCase() === 'torrent added')
|
|
||||||
i18nd = t('TorrentAdded')
|
|
||||||
else if (data.toLowerCase() === 'torrent getting info')
|
|
||||||
i18nd = t('TorrentGettingInfo')
|
|
||||||
else if (data.toLowerCase() === 'torrent preload')
|
|
||||||
i18nd = t('TorrentPreload')
|
|
||||||
else if (data.toLowerCase() === 'torrent working')
|
|
||||||
i18nd = t('TorrentWorking')
|
|
||||||
else if (data.toLowerCase() === 'torrent closed')
|
|
||||||
i18nd = t('TorrentClosed')
|
|
||||||
else if (data.toLowerCase() === 'torrent in db')
|
|
||||||
i18nd = t('TorrentInDb')
|
|
||||||
return <StatisticsField title={t('TorrentStatus')} value={i18nd} iconBg='#aea25b' valueBg='#b4aa6e' icon={BuildIcon} />
|
|
||||||
}
|
|
||||||
|
|
||||||
export const SizeWidget = ({ data }) => {
|
|
||||||
const { t } = useTranslation()
|
|
||||||
return (
|
|
||||||
<StatisticsField
|
|
||||||
title={t('TorrentSize')}
|
|
||||||
value={humanizeSize(data)}
|
|
||||||
iconBg='#9b01ad'
|
|
||||||
valueBg='#ac03bf'
|
|
||||||
icon={ViewAgendaIcon}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,292 +0,0 @@
|
|||||||
import axios from 'axios'
|
|
||||||
import ListItem from '@material-ui/core/ListItem'
|
|
||||||
import ListItemIcon from '@material-ui/core/ListItemIcon'
|
|
||||||
import ListItemText from '@material-ui/core/ListItemText'
|
|
||||||
import { useEffect, useState } from 'react'
|
|
||||||
import SettingsIcon from '@material-ui/icons/Settings'
|
|
||||||
import Dialog from '@material-ui/core/Dialog'
|
|
||||||
import DialogTitle from '@material-ui/core/DialogTitle'
|
|
||||||
import DialogContent from '@material-ui/core/DialogContent'
|
|
||||||
import TextField from '@material-ui/core/TextField'
|
|
||||||
import DialogActions from '@material-ui/core/DialogActions'
|
|
||||||
import Button from '@material-ui/core/Button'
|
|
||||||
import { FormControlLabel, InputLabel, Select, Switch } from '@material-ui/core'
|
|
||||||
import { settingsHost, setTorrServerHost, getTorrServerHost } from 'utils/Hosts'
|
|
||||||
import { useTranslation } from 'react-i18next'
|
|
||||||
|
|
||||||
export default function SettingsDialog() {
|
|
||||||
const { t } = useTranslation()
|
|
||||||
const [open, setOpen] = useState(false)
|
|
||||||
const [settings, setSets] = useState({})
|
|
||||||
const [show, setShow] = useState(false)
|
|
||||||
const [tsHost, setTSHost] = useState(getTorrServerHost())
|
|
||||||
|
|
||||||
const handleClickOpen = () => setOpen(true)
|
|
||||||
const handleClose = () => setOpen(false)
|
|
||||||
const handleSave = () => {
|
|
||||||
setOpen(false)
|
|
||||||
const sets = JSON.parse(JSON.stringify(settings))
|
|
||||||
sets.CacheSize *= 1024 * 1024
|
|
||||||
axios.post(settingsHost(), { action: 'set', sets })
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
axios
|
|
||||||
.post(settingsHost(), { action: 'get' })
|
|
||||||
.then(({ data }) => {
|
|
||||||
setSets({ ...data, CacheSize: data.CacheSize / (1024 * 1024) })
|
|
||||||
setShow(true)
|
|
||||||
})
|
|
||||||
.catch(() => setShow(false))
|
|
||||||
}, [tsHost])
|
|
||||||
|
|
||||||
const onInputHost = event => {
|
|
||||||
const host = event.target.value
|
|
||||||
setTorrServerHost(host)
|
|
||||||
setTSHost(host)
|
|
||||||
}
|
|
||||||
|
|
||||||
const inputForm = ({ target: { type, value, checked, id } }) => {
|
|
||||||
const sets = JSON.parse(JSON.stringify(settings))
|
|
||||||
if (type === 'number' || type === 'select-one') {
|
|
||||||
sets[id] = Number(value)
|
|
||||||
} else if (type === 'checkbox') {
|
|
||||||
if (
|
|
||||||
id === 'DisableTCP' ||
|
|
||||||
id === 'DisableUTP' ||
|
|
||||||
id === 'DisableUPNP' ||
|
|
||||||
id === 'DisableDHT' ||
|
|
||||||
id === 'DisablePEX' ||
|
|
||||||
id === 'DisableUpload'
|
|
||||||
)
|
|
||||||
sets[id] = Boolean(!checked)
|
|
||||||
else sets[id] = Boolean(checked)
|
|
||||||
} else if (type === 'url') {
|
|
||||||
sets[id] = value
|
|
||||||
}
|
|
||||||
setSets(sets)
|
|
||||||
}
|
|
||||||
|
|
||||||
const {
|
|
||||||
CacheSize,
|
|
||||||
PreloadBuffer,
|
|
||||||
ReaderReadAHead,
|
|
||||||
RetrackersMode,
|
|
||||||
TorrentDisconnectTimeout,
|
|
||||||
EnableIPv6,
|
|
||||||
ForceEncrypt,
|
|
||||||
DisableTCP,
|
|
||||||
DisableUTP,
|
|
||||||
DisableUPNP,
|
|
||||||
DisableDHT,
|
|
||||||
DisablePEX,
|
|
||||||
DisableUpload,
|
|
||||||
DownloadRateLimit,
|
|
||||||
UploadRateLimit,
|
|
||||||
ConnectionsLimit,
|
|
||||||
DhtConnectionLimit,
|
|
||||||
PeersListenPort,
|
|
||||||
UseDisk,
|
|
||||||
TorrentsSavePath,
|
|
||||||
RemoveCacheOnDrop,
|
|
||||||
} = settings
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<ListItem button key={t('Settings')} onClick={handleClickOpen}>
|
|
||||||
<ListItemIcon>
|
|
||||||
<SettingsIcon />
|
|
||||||
</ListItemIcon>
|
|
||||||
<ListItemText primary={t('Settings')} />
|
|
||||||
</ListItem>
|
|
||||||
|
|
||||||
<Dialog open={open} onClose={handleClose} aria-labelledby='form-dialog-title' fullWidth>
|
|
||||||
<DialogTitle id='form-dialog-title'>{t('Settings')}</DialogTitle>
|
|
||||||
<DialogContent>
|
|
||||||
<TextField
|
|
||||||
onChange={onInputHost}
|
|
||||||
margin='dense'
|
|
||||||
id='TorrServerHost'
|
|
||||||
label={t('Host')}
|
|
||||||
value={tsHost}
|
|
||||||
type='url'
|
|
||||||
fullWidth
|
|
||||||
/>
|
|
||||||
{show && (
|
|
||||||
<>
|
|
||||||
<TextField
|
|
||||||
onChange={inputForm}
|
|
||||||
margin='dense'
|
|
||||||
id='CacheSize'
|
|
||||||
label={t('CacheSize')}
|
|
||||||
value={CacheSize}
|
|
||||||
type='number'
|
|
||||||
fullWidth
|
|
||||||
/>
|
|
||||||
<br />
|
|
||||||
<TextField
|
|
||||||
onChange={inputForm}
|
|
||||||
margin='dense'
|
|
||||||
id='ReaderReadAHead'
|
|
||||||
label={t('ReaderReadAHead')}
|
|
||||||
value={ReaderReadAHead}
|
|
||||||
type='number'
|
|
||||||
fullWidth
|
|
||||||
/>
|
|
||||||
<br />
|
|
||||||
<FormControlLabel
|
|
||||||
control={<Switch checked={PreloadBuffer} onChange={inputForm} id='PreloadBuffer' color='primary' />}
|
|
||||||
label={t('PreloadBuffer')}
|
|
||||||
/>
|
|
||||||
<br />
|
|
||||||
<FormControlLabel
|
|
||||||
control={<Switch checked={UseDisk} onChange={inputForm} id='UseDisk' color='primary' />}
|
|
||||||
label={t('UseDisk')}
|
|
||||||
/>
|
|
||||||
<br />
|
|
||||||
<small>{t('UseDiskDesc')}</small>
|
|
||||||
<br />
|
|
||||||
<FormControlLabel
|
|
||||||
control={
|
|
||||||
<Switch checked={RemoveCacheOnDrop} onChange={inputForm} id='RemoveCacheOnDrop' color='primary' />
|
|
||||||
}
|
|
||||||
label={t('RemoveCacheOnDrop')}
|
|
||||||
/>
|
|
||||||
<br />
|
|
||||||
<small>{t('RemoveCacheOnDropDesc')}</small>
|
|
||||||
<br />
|
|
||||||
<TextField
|
|
||||||
onChange={inputForm}
|
|
||||||
margin='dense'
|
|
||||||
id='TorrentsSavePath'
|
|
||||||
label={t('TorrentsSavePath')}
|
|
||||||
value={TorrentsSavePath}
|
|
||||||
type='url'
|
|
||||||
fullWidth
|
|
||||||
/>
|
|
||||||
<br />
|
|
||||||
<FormControlLabel
|
|
||||||
control={<Switch checked={EnableIPv6} onChange={inputForm} id='EnableIPv6' color='primary' />}
|
|
||||||
label={t('EnableIPv6')}
|
|
||||||
/>
|
|
||||||
<br />
|
|
||||||
<FormControlLabel
|
|
||||||
control={<Switch checked={!DisableTCP} onChange={inputForm} id='DisableTCP' color='primary' />}
|
|
||||||
label={t('TCP')}
|
|
||||||
/>
|
|
||||||
<br />
|
|
||||||
<FormControlLabel
|
|
||||||
control={<Switch checked={!DisableUTP} onChange={inputForm} id='DisableUTP' color='primary' />}
|
|
||||||
label={t('UTP')}
|
|
||||||
/>
|
|
||||||
<br />
|
|
||||||
<FormControlLabel
|
|
||||||
control={<Switch checked={!DisablePEX} onChange={inputForm} id='DisablePEX' color='primary' />}
|
|
||||||
label={t('PEX')}
|
|
||||||
/>
|
|
||||||
<br />
|
|
||||||
<FormControlLabel
|
|
||||||
control={<Switch checked={ForceEncrypt} onChange={inputForm} id='ForceEncrypt' color='primary' />}
|
|
||||||
label={t('ForceEncrypt')}
|
|
||||||
/>
|
|
||||||
<br />
|
|
||||||
<TextField
|
|
||||||
onChange={inputForm}
|
|
||||||
margin='dense'
|
|
||||||
id='TorrentDisconnectTimeout'
|
|
||||||
label={t('TorrentDisconnectTimeout')}
|
|
||||||
value={TorrentDisconnectTimeout}
|
|
||||||
type='number'
|
|
||||||
fullWidth
|
|
||||||
/>
|
|
||||||
<br />
|
|
||||||
<TextField
|
|
||||||
onChange={inputForm}
|
|
||||||
margin='dense'
|
|
||||||
id='ConnectionsLimit'
|
|
||||||
label={t('ConnectionsLimit')}
|
|
||||||
value={ConnectionsLimit}
|
|
||||||
type='number'
|
|
||||||
fullWidth
|
|
||||||
/>
|
|
||||||
<br />
|
|
||||||
<FormControlLabel
|
|
||||||
control={<Switch checked={!DisableDHT} onChange={inputForm} id='DisableDHT' color='primary' />}
|
|
||||||
label={t('DHT')}
|
|
||||||
/>
|
|
||||||
<br />
|
|
||||||
<TextField
|
|
||||||
onChange={inputForm}
|
|
||||||
margin='dense'
|
|
||||||
id='DhtConnectionLimit'
|
|
||||||
label={t('DhtConnectionLimit')}
|
|
||||||
value={DhtConnectionLimit}
|
|
||||||
type='number'
|
|
||||||
fullWidth
|
|
||||||
/>
|
|
||||||
<br />
|
|
||||||
<TextField
|
|
||||||
onChange={inputForm}
|
|
||||||
margin='dense'
|
|
||||||
id='DownloadRateLimit'
|
|
||||||
label={t('DownloadRateLimit')}
|
|
||||||
value={DownloadRateLimit}
|
|
||||||
type='number'
|
|
||||||
fullWidth
|
|
||||||
/>
|
|
||||||
<br />
|
|
||||||
<FormControlLabel
|
|
||||||
control={<Switch checked={!DisableUpload} onChange={inputForm} id='DisableUpload' color='primary' />}
|
|
||||||
label={t('Upload')}
|
|
||||||
/>
|
|
||||||
<br />
|
|
||||||
<TextField
|
|
||||||
onChange={inputForm}
|
|
||||||
margin='dense'
|
|
||||||
id='UploadRateLimit'
|
|
||||||
label={t('UploadRateLimit')}
|
|
||||||
value={UploadRateLimit}
|
|
||||||
type='number'
|
|
||||||
fullWidth
|
|
||||||
/>
|
|
||||||
<br />
|
|
||||||
<TextField
|
|
||||||
onChange={inputForm}
|
|
||||||
margin='dense'
|
|
||||||
id='PeersListenPort'
|
|
||||||
label={t('PeersListenPort')}
|
|
||||||
value={PeersListenPort}
|
|
||||||
type='number'
|
|
||||||
fullWidth
|
|
||||||
/>
|
|
||||||
<br />
|
|
||||||
<FormControlLabel
|
|
||||||
control={<Switch checked={!DisableUPNP} onChange={inputForm} id='DisableUPNP' color='primary' />}
|
|
||||||
label={t('UPNP')}
|
|
||||||
/>
|
|
||||||
<br />
|
|
||||||
<InputLabel htmlFor='RetrackersMode'>{t('RetrackersMode')}</InputLabel>
|
|
||||||
<Select onChange={inputForm} type='number' native id='RetrackersMode' value={RetrackersMode}>
|
|
||||||
<option value={0}>{t('DontAddRetrackers')}</option>
|
|
||||||
<option value={1}>{t('AddRetrackers')}</option>
|
|
||||||
<option value={2}>{t('RemoveRetrackers')}</option>
|
|
||||||
<option value={3}>{t('ReplaceRetrackers')}</option>
|
|
||||||
</Select>
|
|
||||||
<br />
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</DialogContent>
|
|
||||||
|
|
||||||
<DialogActions>
|
|
||||||
<Button onClick={handleClose} color='primary' variant='outlined'>
|
|
||||||
{t('Cancel')}
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<Button onClick={handleSave} color='primary' variant='outlined'>
|
|
||||||
{t('Save')}
|
|
||||||
</Button>
|
|
||||||
</DialogActions>
|
|
||||||
</Dialog>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
import { useEffect, useRef, useState } from 'react'
|
|
||||||
import { Typography } from '@material-ui/core'
|
|
||||||
import { torrentsHost } from 'utils/Hosts'
|
|
||||||
import TorrentCard from 'components/TorrentCard'
|
|
||||||
import axios from 'axios'
|
|
||||||
import CircularProgress from '@material-ui/core/CircularProgress'
|
|
||||||
import { TorrentListWrapper, CenteredGrid } from 'App/style'
|
|
||||||
import { useTranslation } from 'react-i18next'
|
|
||||||
|
|
||||||
export default function TorrentList() {
|
|
||||||
const { t } = useTranslation()
|
|
||||||
const [torrents, setTorrents] = useState([])
|
|
||||||
const [isLoading, setIsLoading] = useState(true)
|
|
||||||
const [isOffline, setIsOffline] = useState(true)
|
|
||||||
const timerID = useRef(-1)
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
timerID.current = setInterval(() => {
|
|
||||||
// getting torrent list
|
|
||||||
axios
|
|
||||||
.post(torrentsHost(), { action: 'list' })
|
|
||||||
.then(({ data }) => {
|
|
||||||
// updating torrent list
|
|
||||||
setTorrents(data)
|
|
||||||
setIsOffline(false)
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
// resetting torrent list
|
|
||||||
setTorrents([])
|
|
||||||
setIsOffline(true)
|
|
||||||
})
|
|
||||||
.finally(() => setIsLoading(false))
|
|
||||||
}, 1000)
|
|
||||||
|
|
||||||
return () => clearInterval(timerID.current)
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
if (isLoading || isOffline || !torrents.length) {
|
|
||||||
return (
|
|
||||||
<CenteredGrid>
|
|
||||||
{isLoading ? (
|
|
||||||
<CircularProgress />
|
|
||||||
) : isOffline ? (
|
|
||||||
<Typography>{t('Offline')}</Typography>
|
|
||||||
) : (
|
|
||||||
!torrents.length && <Typography>{t('NoTorrentsAdded')}</Typography>
|
|
||||||
)}
|
|
||||||
</CenteredGrid>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<TorrentListWrapper>
|
|
||||||
{torrents.map(torrent => (
|
|
||||||
<TorrentCard key={torrent.hash} torrent={torrent} />
|
|
||||||
))}
|
|
||||||
</TorrentListWrapper>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
import ListItemIcon from '@material-ui/core/ListItemIcon'
|
|
||||||
import ListItemText from '@material-ui/core/ListItemText'
|
|
||||||
import ListItem from '@material-ui/core/ListItem'
|
|
||||||
import PublishIcon from '@material-ui/icons/Publish'
|
|
||||||
import { torrentUploadHost } from 'utils/Hosts'
|
|
||||||
import axios from 'axios'
|
|
||||||
import { useTranslation } from 'react-i18next'
|
|
||||||
|
|
||||||
export default function UploadDialog() {
|
|
||||||
const { t } = useTranslation()
|
|
||||||
const handleCapture = ({ target: { files } }) => {
|
|
||||||
const [file] = files
|
|
||||||
const data = new FormData()
|
|
||||||
data.append('save', 'true')
|
|
||||||
data.append('file', file)
|
|
||||||
axios.post(torrentUploadHost(), data)
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<label htmlFor='raised-button-file'>
|
|
||||||
<input onChange={handleCapture} accept='*/*' type='file' style={{ display: 'none' }} id='raised-button-file' />
|
|
||||||
|
|
||||||
<ListItem button variant='raised' type='submit' component='span' key={t('UploadFile')}>
|
|
||||||
<ListItemIcon>
|
|
||||||
<PublishIcon />
|
|
||||||
</ListItemIcon>
|
|
||||||
|
|
||||||
<ListItemText primary={t('UploadFile')} />
|
|
||||||
</ListItem>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user