mirror of
https://github.com/Ernous/TorrServerJellyfin.git
synced 2025-12-19 05:26:09 +05:00
merge maser
This commit is contained in:
18
build-all.sh
18
build-all.sh
@@ -29,6 +29,15 @@ set_goarm() {
|
||||
GO_ARM=""
|
||||
fi
|
||||
}
|
||||
# use softfloat for mips builds
|
||||
set_gomips() {
|
||||
if [[ "$1" =~ mips ]]; then
|
||||
if [[ "$1" =~ mips(64) ]]; then MIPS64="${BASH_REMATCH[1]}"; fi
|
||||
GO_MIPS="GOMIPS${MIPS64}=softfloat"
|
||||
else
|
||||
GO_MIPS=""
|
||||
fi
|
||||
}
|
||||
|
||||
GOBIN="go"
|
||||
|
||||
@@ -59,11 +68,15 @@ for PLATFORM in "${PLATFORMS[@]}"; do
|
||||
GOOS=${PLATFORM%/*}
|
||||
GOARCH=${PLATFORM#*/}
|
||||
set_goarm "$GOARCH"
|
||||
set_gomips "$GOARCH"
|
||||
BIN_FILENAME="${OUTPUT}-${GOOS}-${GOARCH}${GOARM}"
|
||||
if [[ "${GOOS}" == "windows" ]]; then BIN_FILENAME="${BIN_FILENAME}.exe"; fi
|
||||
CMD="GOOS=${GOOS} GOARCH=${GOARCH} ${GO_ARM} ${GOBIN} build ${BUILD_FLAGS} -o ${BIN_FILENAME} ./cmd"
|
||||
CMD="GOOS=${GOOS} GOARCH=${GOARCH} ${GO_ARM} ${GO_MIPS} ${GOBIN} build ${BUILD_FLAGS} -o ${BIN_FILENAME} ./cmd"
|
||||
echo "${CMD}"
|
||||
eval "$CMD" || FAILURES="${FAILURES} ${GOOS}/${GOARCH}${GOARM}"
|
||||
CMD="../upx -q ${BIN_FILENAME}"; # upx --brute produce much smaller binaries
|
||||
echo "compress with ${CMD}"
|
||||
eval "$CMD"
|
||||
done
|
||||
|
||||
#####################################
|
||||
@@ -89,6 +102,9 @@ for GOARCH in "${!COMPILERS[@]}"; do
|
||||
CMD="GOOS=${GOOS} GOARCH=${GOARCH} ${GO_ARM} CGO_ENABLED=1 ${GOBIN} build ${BUILD_FLAGS} -o ${BIN_FILENAME} ./cmd"
|
||||
echo "${CMD}"
|
||||
eval "${CMD}" || FAILURES="${FAILURES} ${GOOS}/${GOARCH}${GOARM}"
|
||||
CMD="../upx -q ${BIN_FILENAME}"; # upx --brute produce much smaller binaries
|
||||
echo "compress with ${CMD}"
|
||||
eval "$CMD"
|
||||
done
|
||||
|
||||
# eval errors
|
||||
|
||||
@@ -6,5 +6,5 @@ ROOT=${PWD}
|
||||
echo "Build web"
|
||||
go run gen_web.go
|
||||
|
||||
sudo docker run --rm -v "$PWD":/usr/src/torr -v ~/go/pkg/mod:/go/pkg/mod -w /usr/src/torr golang:1.16 ./build-all.sh
|
||||
sudo docker run --rm -v "$PWD":/usr/src/torr -v ~/go/pkg/mod:/go/pkg/mod -w /usr/src/torr golang:1.16.7-stretch ./build-all.sh
|
||||
sudo chmod 0777 ./dist/*
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -23,14 +24,14 @@ import (
|
||||
|
||||
type args struct {
|
||||
Port string `arg:"-p" help:"web server port"`
|
||||
Path string `arg:"-d" help:"database path"`
|
||||
LogPath string `arg:"-l" help:"log path"`
|
||||
WebLogPath string `arg:"-w" help:"web log path"`
|
||||
Path string `arg:"-d" help:"database dir path"`
|
||||
LogPath string `arg:"-l" help:"server log file path"`
|
||||
WebLogPath string `arg:"-w" help:"web access log file path"`
|
||||
RDB bool `arg:"-r" help:"start in read-only DB mode"`
|
||||
HttpAuth bool `arg:"-a" help:"http auth on all requests"`
|
||||
DontKill bool `arg:"-k" help:"dont kill server on signal"`
|
||||
UI bool `arg:"-u" help:"run page torrserver in browser"`
|
||||
TorrentsDir string `arg:"-t" help:"autoload torrent from dir"`
|
||||
HttpAuth bool `arg:"-a" help:"enable http auth on all requests"`
|
||||
DontKill bool `arg:"-k" help:"don't kill server on signal"`
|
||||
UI bool `arg:"-u" help:"open torrserver page in browser"`
|
||||
TorrentsDir string `arg:"-t" help:"autoload torrents from dir"`
|
||||
}
|
||||
|
||||
func (args) Version() string {
|
||||
@@ -53,6 +54,11 @@ func main() {
|
||||
settings.Path = params.Path
|
||||
settings.HttpAuth = params.HttpAuth
|
||||
log.Init(params.LogPath, params.WebLogPath)
|
||||
fmt.Println("=========== START ===========")
|
||||
fmt.Println("Build Go version:", runtime.Version())
|
||||
if params.HttpAuth {
|
||||
log.TLogln("Use HTTP Auth file", settings.Path+"/accs.db")
|
||||
}
|
||||
|
||||
dnsResolve()
|
||||
Preconfig(params.DontKill)
|
||||
@@ -78,7 +84,7 @@ func main() {
|
||||
func dnsResolve() {
|
||||
addrs, err := net.LookupHost("www.google.com")
|
||||
if len(addrs) == 0 {
|
||||
fmt.Println("Check dns", addrs, err)
|
||||
log.TLogln("Check dns failed", addrs, err)
|
||||
|
||||
fn := func(ctx context.Context, network, address string) (net.Conn, error) {
|
||||
d := net.Dialer{}
|
||||
@@ -89,8 +95,10 @@ func dnsResolve() {
|
||||
Dial: fn,
|
||||
}
|
||||
|
||||
addrs, err = net.LookupHost("www.themoviedb.org")
|
||||
fmt.Println("Check new dns", addrs, err)
|
||||
addrs, err = net.LookupHost("www.google.com")
|
||||
log.TLogln("Check cloudflare dns", addrs, err)
|
||||
} else {
|
||||
log.TLogln("Check dns OK", addrs, err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,10 +3,11 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
"server/log"
|
||||
)
|
||||
|
||||
func Preconfig(dkill bool) {
|
||||
@@ -15,15 +16,14 @@ func Preconfig(dkill bool) {
|
||||
signal.Notify(sigc,
|
||||
syscall.SIGHUP,
|
||||
syscall.SIGINT,
|
||||
|
||||
syscall.SIGPIPE,
|
||||
syscall.SIGTERM,
|
||||
syscall.SIGQUIT)
|
||||
go func() {
|
||||
for s := range sigc {
|
||||
if dkill {
|
||||
fmt.Println("Signal catched:", s)
|
||||
fmt.Println("For stop server, close in api")
|
||||
log.TLogln("Signal catched:", s)
|
||||
log.TLogln("To stop server, close it from web / api")
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
@@ -5,7 +5,7 @@ replace (
|
||||
github.com/anacrolix/torrent v1.2.6 => github.com/yourok/torrent v0.0.0-20210427182412-1ef78c0fb02c
|
||||
)
|
||||
|
||||
go 1.16
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/alexflint/go-arg v1.3.0
|
||||
@@ -18,4 +18,3 @@ require (
|
||||
github.com/pkg/errors v0.8.1
|
||||
go.etcd.io/bbolt v1.3.5
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4
|
||||
)
|
||||
|
||||
@@ -35,7 +35,7 @@ func Init(path, webpath string) {
|
||||
|
||||
if path != "" {
|
||||
if fi, err := os.Lstat(path); err == nil {
|
||||
if fi.Size() >= 1*1024*1024*1024 {
|
||||
if fi.Size() >= 100*1024*1024 { // 100MB
|
||||
os.Remove(path)
|
||||
}
|
||||
}
|
||||
@@ -47,6 +47,10 @@ func Init(path, webpath string) {
|
||||
logFile = ff
|
||||
os.Stdout = ff
|
||||
os.Stderr = ff
|
||||
//https://stackoverflow.com/a/36140590
|
||||
//fmt.Print(time.Now().UTC().Format("2006-01-02T15:04:05.999Z") + " TLOG " + string(bytes))
|
||||
log.SetFlags(log.Ldate|log.Ltime|log.LUTC|log.Lmsgprefix)
|
||||
log.SetPrefix("0000 TLOG ")
|
||||
log.SetOutput(ff)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package server
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
@@ -15,8 +16,18 @@ func Start(port string, roSets bool) {
|
||||
if port == "" {
|
||||
port = "8090"
|
||||
}
|
||||
go cleanCache()
|
||||
web.Start(port)
|
||||
log.TLogln("Check web port", port)
|
||||
l, err := net.Listen("tcp", ":"+port)
|
||||
if l != nil {
|
||||
l.Close()
|
||||
}
|
||||
if err != nil {
|
||||
log.TLogln("Port", port, "already in use! Abort")
|
||||
os.Exit(1)
|
||||
} else {
|
||||
go cleanCache()
|
||||
web.Start(port)
|
||||
}
|
||||
}
|
||||
|
||||
func cleanCache() {
|
||||
|
||||
@@ -40,7 +40,6 @@ type BTSets struct {
|
||||
ConnectionsLimit int
|
||||
DhtConnectionLimit int // 0 - inf
|
||||
PeersListenPort int
|
||||
//Strategy int // 0 - RequestStrategyDuplicateRequestTimeout, 1 - RequestStrategyFuzzing, 2 - RequestStrategyFastest
|
||||
}
|
||||
|
||||
func (v *BTSets) String() string {
|
||||
|
||||
@@ -209,6 +209,7 @@ func SetDefSettings() {
|
||||
func Shutdown() {
|
||||
bts.Disconnect()
|
||||
sets.CloseDB()
|
||||
log.TLogln("Received shutdown. Quit")
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
|
||||
@@ -61,6 +61,7 @@ func (bt *BTServer) configure() {
|
||||
|
||||
userAgent := "qBittorrent/4.3.2"
|
||||
peerID := "-qB4320-"
|
||||
upnpID := "TorrServer"
|
||||
cliVers := userAgent //"uTorrent/2210(25302)"
|
||||
|
||||
bt.config.Debug = settings.BTsets.EnableDebug
|
||||
@@ -74,6 +75,7 @@ func (bt *BTServer) configure() {
|
||||
bt.config.IPBlocklist = blocklist
|
||||
bt.config.Bep20 = peerID
|
||||
bt.config.PeerID = utils.PeerIDRandom(peerID)
|
||||
bt.config.UpnpID = upnpID
|
||||
bt.config.HTTPUserAgent = userAgent
|
||||
bt.config.ExtendedHandshakeClientVersion = cliVers
|
||||
bt.config.EstablishedConnsPerTorrent = settings.BTsets.ConnectionsLimit
|
||||
@@ -93,11 +95,12 @@ func (bt *BTServer) configure() {
|
||||
bt.config.UploadRateLimiter = utils.Limit(settings.BTsets.UploadRateLimit * 1024)
|
||||
}
|
||||
if settings.BTsets.PeersListenPort > 0 {
|
||||
log.Println("Set listen port", settings.BTsets.PeersListenPort)
|
||||
bt.config.ListenPort = settings.BTsets.PeersListenPort
|
||||
} else {
|
||||
log.Println("Find upnp port")
|
||||
upnpport := 32000
|
||||
for {
|
||||
log.Println("Check upnp port", upnpport)
|
||||
l, err := net.Listen("tcp", ":"+strconv.Itoa(upnpport))
|
||||
if l != nil {
|
||||
l.Close()
|
||||
@@ -111,7 +114,7 @@ func (bt *BTServer) configure() {
|
||||
bt.config.ListenPort = upnpport
|
||||
}
|
||||
|
||||
log.Println("Configure client:", settings.BTsets)
|
||||
log.Println("Client config:", settings.BTsets)
|
||||
}
|
||||
|
||||
func (bt *BTServer) GetTorrent(hash torrent.InfoHash) *Torrent {
|
||||
|
||||
@@ -63,7 +63,11 @@ func (t *Torrent) Preload(index int, size int64) {
|
||||
}
|
||||
}()
|
||||
|
||||
mb5 := int64(5 * 1024 * 1024)
|
||||
// mb5 -> 8/16 MB
|
||||
mb5 := int64(t.Info().PieceLength)
|
||||
if (mb5 < 8 * 1024 * 1024) {
|
||||
mb5 = 8 * 1024 * 1024
|
||||
}
|
||||
|
||||
readerStart := file.NewReader()
|
||||
defer readerStart.Close()
|
||||
|
||||
@@ -20,6 +20,7 @@ type ItemState struct {
|
||||
Length int64
|
||||
Size int64
|
||||
Completed bool
|
||||
Priority int
|
||||
}
|
||||
|
||||
type ReaderState struct {
|
||||
|
||||
@@ -35,12 +35,11 @@ type Cache struct {
|
||||
muReaders sync.Mutex
|
||||
|
||||
isRemove bool
|
||||
isClosed bool
|
||||
muRemove sync.Mutex
|
||||
torrent *torrent.Torrent
|
||||
}
|
||||
|
||||
const FileRangeNotDelete = 5 * 1024 * 1024
|
||||
|
||||
func NewCache(capacity int64, storage *Storage) *Cache {
|
||||
ret := &Cache{
|
||||
capacity: capacity,
|
||||
@@ -89,6 +88,8 @@ func (c *Cache) Piece(m metainfo.Piece) storage.PieceImpl {
|
||||
|
||||
func (c *Cache) Close() error {
|
||||
log.TLogln("Close cache for:", c.hash)
|
||||
c.isClosed = true
|
||||
|
||||
delete(c.storage.caches, c.hash)
|
||||
|
||||
if settings.BTsets.RemoveCacheOnDrop {
|
||||
@@ -114,14 +115,16 @@ func (c *Cache) Close() error {
|
||||
}
|
||||
|
||||
func (c *Cache) removePiece(piece *Piece) {
|
||||
piece.Release()
|
||||
if !c.isClosed {
|
||||
piece.Release()
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Cache) AdjustRA(readahead int64) {
|
||||
if settings.BTsets.CacheSize == 0 {
|
||||
c.capacity = readahead * 3
|
||||
}
|
||||
if len(c.readers) > 0 {
|
||||
if c.Readers() > 0 {
|
||||
c.muReaders.Lock()
|
||||
for r, _ := range c.readers {
|
||||
r.SetReadahead(readahead)
|
||||
@@ -145,14 +148,16 @@ func (c *Cache) GetState() *state.CacheState {
|
||||
Size: p.Size,
|
||||
Length: c.pieceLength,
|
||||
Completed: p.Complete,
|
||||
Priority: int(c.torrent.PieceState(p.Id).Priority),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
readersState := make([]*state.ReaderState, 0)
|
||||
c.muReaders.Lock()
|
||||
if len(c.readers) > 0 {
|
||||
|
||||
if c.Readers() > 0 {
|
||||
c.muReaders.Lock()
|
||||
for r, _ := range c.readers {
|
||||
rng := r.getPiecesRange()
|
||||
pc := r.getReaderPiece()
|
||||
@@ -162,8 +167,8 @@ func (c *Cache) GetState() *state.CacheState {
|
||||
Reader: pc,
|
||||
})
|
||||
}
|
||||
c.muReaders.Unlock()
|
||||
}
|
||||
c.muReaders.Unlock()
|
||||
|
||||
c.filled = fill
|
||||
cState.Capacity = c.capacity
|
||||
@@ -177,7 +182,7 @@ func (c *Cache) GetState() *state.CacheState {
|
||||
}
|
||||
|
||||
func (c *Cache) cleanPieces() {
|
||||
if c.isRemove {
|
||||
if c.isRemove || c.isClosed {
|
||||
return
|
||||
}
|
||||
c.muRemove.Lock()
|
||||
@@ -236,24 +241,37 @@ func (c *Cache) getRemPieces() []*Piece {
|
||||
|
||||
c.updatePriority()
|
||||
|
||||
c.muReaders.Lock()
|
||||
for r, _ := range c.readers {
|
||||
if c.isIdInFileBE(ranges, r.getReaderPiece()) {
|
||||
continue
|
||||
}
|
||||
pc := r.getReaderPiece()
|
||||
readerPos := r.getReaderPiece()
|
||||
readerRAHPos := r.getReaderRAHPiece()
|
||||
end := r.getPiecesRange().End
|
||||
limit := 5
|
||||
|
||||
for pc <= end && limit > 0 {
|
||||
if !c.pieces[pc].Complete {
|
||||
if c.torrent.PieceState(pc).Priority == torrent.PiecePriorityNone {
|
||||
c.torrent.Piece(pc).SetPriority(torrent.PiecePriorityNormal)
|
||||
count := int(16 * 1024 * 1024 / c.pieceLength * 5) // 80 MB
|
||||
if count > 40 {
|
||||
count = 40
|
||||
}
|
||||
limit := 0
|
||||
for i := readerPos; i < end && limit < count; i++ {
|
||||
if !c.pieces[i].Complete {
|
||||
if i == readerPos {
|
||||
c.torrent.Piece(i).SetPriority(torrent.PiecePriorityNow)
|
||||
} else if i == readerPos + 1 {
|
||||
c.torrent.Piece(i).SetPriority(torrent.PiecePriorityNext)
|
||||
} else if i > readerPos && i <= readerRAHPos {
|
||||
c.torrent.Piece(i).SetPriority(torrent.PiecePriorityReadahead)
|
||||
} else if i > readerRAHPos && i <= readerPos + (end - readerPos)/2 && c.torrent.PieceState(i).Priority != torrent.PiecePriorityHigh {
|
||||
c.torrent.Piece(i).SetPriority(torrent.PiecePriorityHigh)
|
||||
} else if i > readerPos + (end - readerPos)/2 && c.torrent.PieceState(i).Priority != torrent.PiecePriorityNormal {
|
||||
c.torrent.Piece(i).SetPriority(torrent.PiecePriorityNormal)
|
||||
}
|
||||
limit--
|
||||
limit++
|
||||
}
|
||||
pc++
|
||||
}
|
||||
}
|
||||
c.muReaders.Unlock()
|
||||
|
||||
sort.Slice(piecesRemove, func(i, j int) bool {
|
||||
return piecesRemove[i].Accessed < piecesRemove[j].Accessed
|
||||
@@ -264,14 +282,21 @@ func (c *Cache) getRemPieces() []*Piece {
|
||||
}
|
||||
|
||||
func (c *Cache) isIdInFileBE(ranges []Range, id int) bool {
|
||||
|
||||
// keep 8/16 MB
|
||||
FileRangeNotDelete := int64(c.pieceLength)
|
||||
if (FileRangeNotDelete < 8 * 1024 * 1024) {
|
||||
FileRangeNotDelete = 8 * 1024 * 1024
|
||||
}
|
||||
|
||||
for _, rng := range ranges {
|
||||
ss := int(rng.File.Offset() / c.pieceLength)
|
||||
se := int((FileRangeNotDelete + rng.File.Offset()) / c.pieceLength)
|
||||
se := int((rng.File.Offset() + FileRangeNotDelete) / c.pieceLength)
|
||||
|
||||
es := int((rng.File.Offset() + rng.File.Length() - FileRangeNotDelete) / c.pieceLength)
|
||||
ee := int((rng.File.Offset() + rng.File.Length()) / c.pieceLength)
|
||||
|
||||
if id >= ss && id <= se || id >= es && id <= ee {
|
||||
if id >= ss && id < se || id > es && id <= ee {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,5 +73,10 @@ func (p *Piece) Release() {
|
||||
} else {
|
||||
p.dPiece.Release()
|
||||
}
|
||||
|
||||
// TODO: check this merge
|
||||
if !p.cache.isClosed {
|
||||
p.cache.torrent.Piece(p.Id).SetPriority(torrent.PiecePriorityNone)
|
||||
// fix remove pieces hash
|
||||
p.cache.torrent.Piece(p.Id).UpdateCompletion()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,7 +114,7 @@ func (r *Reader) Close() {
|
||||
// this struct close in cache
|
||||
r.isClosed = true
|
||||
if len(r.file.Torrent().Files()) > 0 {
|
||||
r.Reader.Close()
|
||||
r.Reader.Close()
|
||||
}
|
||||
go r.cache.getRemPieces()
|
||||
}
|
||||
@@ -125,8 +125,11 @@ func (r *Reader) getPiecesRange() Range {
|
||||
}
|
||||
|
||||
func (r *Reader) getReaderPiece() int {
|
||||
readerOff := r.offset
|
||||
return r.getPieceNum(readerOff)
|
||||
return r.getPieceNum(r.offset)
|
||||
}
|
||||
|
||||
func (r *Reader) getReaderRAHPiece() int {
|
||||
return r.getPieceNum(r.offset + r.readahead)
|
||||
}
|
||||
|
||||
func (r *Reader) getPieceNum(offset int64) int {
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
@@ -46,8 +47,13 @@ func (t *Torrent) Stream(fileID int, req *http.Request, resp http.ResponseWriter
|
||||
|
||||
reader := t.NewReader(file)
|
||||
|
||||
log.Println("Connect client")
|
||||
|
||||
host, port, err := net.SplitHostPort(req.RemoteAddr)
|
||||
if err != nil {
|
||||
log.Println("Connect client")
|
||||
} else {
|
||||
log.Println("Connect client", host, port)
|
||||
}
|
||||
|
||||
sets.SetViewed(&sets.Viewed{t.Hash().HexString(), fileID})
|
||||
|
||||
resp.Header().Set("Connection", "close")
|
||||
@@ -56,6 +62,10 @@ func (t *Torrent) Stream(fileID int, req *http.Request, resp http.ResponseWriter
|
||||
http.ServeContent(resp, req, file.Path(), time.Unix(t.Timestamp, 0), reader)
|
||||
|
||||
t.CloseReader(reader)
|
||||
log.Println("Disconnect client")
|
||||
if err != nil {
|
||||
log.Println("Disconnect client")
|
||||
} else {
|
||||
log.Println("Disconnect client", host, port)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
package version
|
||||
|
||||
const Version = "MatriX.102.OE"
|
||||
const Version = "MatriX.103.OE"
|
||||
|
||||
@@ -71,7 +71,7 @@ func addTorrent(req torrReqJS, c *gin.Context) {
|
||||
req.Link = strings.ReplaceAll(req.Link, "&", "&")
|
||||
torrSpec, err := utils.ParseLink(req.Link)
|
||||
if err != nil {
|
||||
log.TLogln("error add torrent:", err)
|
||||
log.TLogln("error parse link:", err)
|
||||
c.AbortWithError(http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
@@ -85,12 +85,18 @@ func addTorrent(req torrReqJS, c *gin.Context) {
|
||||
|
||||
go func() {
|
||||
if !tor.GotInfo() {
|
||||
log.TLogln("error add torrent:", "timeout connection torrent")
|
||||
log.TLogln("error add torrent:", "timeout connection get torrent info")
|
||||
return
|
||||
}
|
||||
|
||||
if tor.Title == "" {
|
||||
tor.Title = tor.Name()
|
||||
tor.Title = torrSpec.DisplayName // prefer dn over name
|
||||
tor.Title = strings.ReplaceAll(tor.Title, "rutor.info", "")
|
||||
tor.Title = strings.ReplaceAll(tor.Title, "_", " ")
|
||||
tor.Title = strings.Trim(tor.Title, " ")
|
||||
if tor.Title == "" {
|
||||
tor.Title = tor.Name()
|
||||
}
|
||||
}
|
||||
|
||||
if req.SaveToDB {
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
object-assign
|
||||
(c) Sindre Sorhus
|
||||
@license MIT
|
||||
*/
|
||||
|
||||
/*!
|
||||
* The buffer module from node.js, for the browser.
|
||||
*
|
||||
* @author Feross Aboukhadijeh <http://feross.org>
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
/*! blob-to-buffer. MIT License. Feross Aboukhadijeh <https://feross.org/opensource> */
|
||||
|
||||
/*! https://mths.be/punycode v1.4.1 by @mathias */
|
||||
|
||||
/*! ieee754. BSD-3-Clause License. Feross Aboukhadijeh <https://feross.org/opensource> */
|
||||
|
||||
/*! magnet-uri. MIT License. WebTorrent LLC <https://webtorrent.io/opensource> */
|
||||
|
||||
/*! parse-torrent. MIT License. WebTorrent LLC <https://webtorrent.io/opensource> */
|
||||
|
||||
/*! queue-microtask. MIT License. Feross Aboukhadijeh <https://feross.org/opensource> */
|
||||
|
||||
/*! safe-buffer. MIT License. Feross Aboukhadijeh <https://feross.org/opensource> */
|
||||
|
||||
/*! simple-concat. MIT License. Feross Aboukhadijeh <https://feross.org/opensource> */
|
||||
|
||||
/*! simple-get. MIT License. Feross Aboukhadijeh <https://feross.org/opensource> */
|
||||
|
||||
/**
|
||||
* A better abstraction over CSS.
|
||||
*
|
||||
* @copyright Oleg Isonen (Slobodskoi) / Isonen 2014-present
|
||||
* @website https://github.com/cssinjs/jss
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
/** @license React v0.20.2
|
||||
* scheduler.production.min.js
|
||||
*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
/** @license React v16.13.1
|
||||
* react-is.production.min.js
|
||||
*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
/** @license React v17.0.2
|
||||
* react-dom.production.min.js
|
||||
*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
/** @license React v17.0.2
|
||||
* react-is.production.min.js
|
||||
*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
/** @license React v17.0.2
|
||||
* react-jsx-runtime.production.min.js
|
||||
*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
/** @license React v17.0.2
|
||||
* react.production.min.js
|
||||
*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -51,7 +51,7 @@ func Start(port string) {
|
||||
api.SetupRoute(&route.RouterGroup)
|
||||
pages.SetupRoute(&route.RouterGroup)
|
||||
}
|
||||
log.TLogln("Start web", port)
|
||||
log.TLogln("Start web server at port", port)
|
||||
waitChan <- route.Run(":" + port)
|
||||
}
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ export const Section = styled.section`
|
||||
padding: 20px;
|
||||
|
||||
> span {
|
||||
font-size: 20px;
|
||||
font-size: 22px;
|
||||
display: block;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
@@ -87,7 +87,7 @@ export const LinkWrapper = styled.a`
|
||||
align-items: center;
|
||||
justify-content: start;
|
||||
border: 1px solid;
|
||||
padding: 10px;
|
||||
padding: 7px 10px;
|
||||
border-radius: 5px;
|
||||
text-transform: uppercase;
|
||||
text-decoration: none;
|
||||
@@ -105,7 +105,7 @@ export const LinkWrapper = styled.a`
|
||||
filter: brightness(1.1);
|
||||
|
||||
> * {
|
||||
transform: translateY(-1px);
|
||||
transform: translateY(0px);
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
@@ -14,7 +14,7 @@ import { getTorrents } from 'utils/Utils'
|
||||
import parseTorrent from 'parse-torrent'
|
||||
import { ButtonWrapper, Header } from 'style/DialogStyles'
|
||||
|
||||
import { checkImageURL, getMoviePosters, chechTorrentSource, parseTorrentTitle } from './helpers'
|
||||
import { checkImageURL, getMoviePosters, checkTorrentSource, parseTorrentTitle } from './helpers'
|
||||
import { Content } from './style'
|
||||
import RightSideComponent from './RightSideComponent'
|
||||
import LeftSideComponent from './LeftSideComponent'
|
||||
@@ -147,7 +147,7 @@ export default function AddDialog({
|
||||
const prevTorrentSourceState = usePreviousState(torrentSource)
|
||||
|
||||
useEffect(() => {
|
||||
const isCorrectSource = chechTorrentSource(torrentSource)
|
||||
const isCorrectSource = checkTorrentSource(torrentSource)
|
||||
if (!isCorrectSource) return setIsTorrentSourceCorrect(false)
|
||||
|
||||
setIsTorrentSourceCorrect(true)
|
||||
@@ -227,10 +227,10 @@ export default function AddDialog({
|
||||
{!isEditMode && (
|
||||
<LeftSideComponent
|
||||
setIsUserInteractedWithPoster={setIsUserInteractedWithPoster}
|
||||
selectedFile={selectedFile}
|
||||
setSelectedFile={setSelectedFile}
|
||||
torrentSource={torrentSource}
|
||||
setTorrentSource={setTorrentSource}
|
||||
selectedFile={selectedFile}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -271,7 +271,7 @@ export default function AddDialog({
|
||||
style={{ minWidth: '110px' }}
|
||||
disabled={!torrentSource || (isHashAlreadyExists && !isEditMode) || !isTorrentSourceCorrect}
|
||||
onClick={handleSave}
|
||||
color='primary'
|
||||
color='secondary'
|
||||
>
|
||||
{isSaving ? <CircularProgress style={{ color: 'white' }} size={20} /> : t(isEditMode ? 'Save' : 'Add')}
|
||||
</Button>
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { rgba } from 'polished'
|
||||
import { NoImageIcon } from 'icons'
|
||||
import { IconButton, InputAdornment, TextField, useTheme } from '@material-ui/core'
|
||||
import { CheckBox as CheckBoxIcon } from '@material-ui/icons'
|
||||
import { HighlightOff as HighlightOffIcon } from '@material-ui/icons'
|
||||
|
||||
import {
|
||||
ClearPosterButton,
|
||||
@@ -84,7 +85,7 @@ export default function RightSideComponent({
|
||||
endAdornment: (
|
||||
<InputAdornment position='end'>
|
||||
<IconButton
|
||||
style={{ padding: '0 0 0 7px' }}
|
||||
style={{ padding: '1px' }}
|
||||
onClick={() => {
|
||||
setTitle('')
|
||||
setIsCustomTitleEnabled(!isCustomTitleEnabled)
|
||||
@@ -92,7 +93,7 @@ export default function RightSideComponent({
|
||||
setIsUserInteractedWithPoster(false)
|
||||
}}
|
||||
>
|
||||
<CheckBoxIcon style={{ color: isCustomTitleEnabled ? primary : 'gray' }} />
|
||||
<HighlightOffIcon style={{ color: isCustomTitleEnabled ? primary : rgba('#ccc', 0.5) }} />
|
||||
</IconButton>
|
||||
</InputAdornment>
|
||||
),
|
||||
|
||||
@@ -22,20 +22,19 @@ export const getMoviePosters = (movieName, language = 'en') => {
|
||||
|
||||
export const checkImageURL = async url => {
|
||||
if (!url || !url.match(/.(jpg|jpeg|png|gif)$/i)) return false
|
||||
|
||||
try {
|
||||
await fetch(url, { mode: 'no-cors' })
|
||||
return true
|
||||
} catch (e) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
const magnetRegex = /^magnet:\?xt=urn:[a-z0-9].*/i
|
||||
export 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
|
||||
export const chechTorrentSource = source =>
|
||||
source.match(hashRegex) !== null || source.match(magnetRegex) !== null || source.match(torrentRegex) !== null
|
||||
const linkRegex = /^(http(s?)):\/\/.*/i
|
||||
|
||||
export const checkTorrentSource = source =>
|
||||
source.match(hashRegex) !== null ||
|
||||
source.match(magnetRegex) !== null ||
|
||||
source.match(torrentRegex) !== null ||
|
||||
source.match(linkRegex) !== null
|
||||
|
||||
export const parseTorrentTitle = (parsingSource, callback) => {
|
||||
parseTorrent.remote(parsingSource, (err, { name, files } = {}) => {
|
||||
|
||||
@@ -1,34 +1,31 @@
|
||||
import Button from '@material-ui/core/Button'
|
||||
import { AppBar, IconButton, makeStyles, Toolbar, Typography } from '@material-ui/core'
|
||||
import CloseIcon from '@material-ui/icons/Close'
|
||||
import { ArrowBack } from '@material-ui/icons'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
const useStyles = makeStyles({
|
||||
appBar: { position: 'relative' },
|
||||
title: { marginLeft: '6px', flex: 1 },
|
||||
}))
|
||||
title: { marginLeft: '5px', flex: 1 },
|
||||
})
|
||||
|
||||
export default function DialogHeader({ title, onClose, onBack }) {
|
||||
const { t } = useTranslation()
|
||||
const classes = useStyles()
|
||||
|
||||
return (
|
||||
<AppBar className={classes.appBar}>
|
||||
<Toolbar>
|
||||
<IconButton edge='start' color='inherit' onClick={onBack || onClose} aria-label='close'>
|
||||
{onBack ? <ArrowBack /> : <CloseIcon />}
|
||||
</IconButton>
|
||||
{onBack && (
|
||||
<IconButton edge='start' color='inherit' onClick={onBack} aria-label='back'>
|
||||
<ArrowBack />
|
||||
</IconButton>
|
||||
)}
|
||||
|
||||
<Typography variant='h6' className={classes.title}>
|
||||
{title}
|
||||
</Typography>
|
||||
|
||||
{onBack && (
|
||||
<Button autoFocus color='inherit' onClick={onClose}>
|
||||
{t('Close')}
|
||||
</Button>
|
||||
)}
|
||||
<IconButton autoFocus color='inherit' onClick={onClose} aria-label='close' style={{ marginRight: '-10px' }}>
|
||||
<CloseIcon />
|
||||
</IconButton>
|
||||
</Toolbar>
|
||||
</AppBar>
|
||||
)
|
||||
|
||||
@@ -62,9 +62,10 @@ const TorrentCache = ({ cache, isMini }) => {
|
||||
|
||||
ctx.clearRect(0, 0, canvasWidth, height)
|
||||
|
||||
source.forEach(({ percentage, isReader, isReaderRange }, i) => {
|
||||
source.forEach(({ percentage, priority, isReader, isReaderRange }, i) => {
|
||||
const inProgress = percentage > 0 && percentage < 100
|
||||
const isCompleted = percentage === 100
|
||||
const peacePriority = priority
|
||||
const currentRow = i % piecesInOneRow
|
||||
const currentColumn = Math.floor(i / piecesInOneRow)
|
||||
const fixBlurStroke = borderWidth % 2 === 0 ? 0 : 0.5
|
||||
@@ -90,6 +91,20 @@ const TorrentCache = ({ cache, isMini }) => {
|
||||
ctx.fillRect(0, 0, pieceSize, pieceSize)
|
||||
ctx.strokeRect(0, 0, pieceSize, pieceSize)
|
||||
ctx.setTransform(1, 0, 0, 1, 0, 0)
|
||||
|
||||
if (peacePriority > 0) {
|
||||
let info = ''
|
||||
if (peacePriority === 1) info = '*'
|
||||
else if (peacePriority === 2) info = 'H'
|
||||
else if (peacePriority === 3) info = 'R'
|
||||
else if (peacePriority === 4) info = 'N'
|
||||
else if (peacePriority === 5) info = 'A'
|
||||
ctx.font = isMini ? '12px monospace' : '10px monospace'
|
||||
const xpad = isMini ? pieceSize * 0.34 : pieceSize * 0.28
|
||||
const ypad = isMini ? pieceSize * 0.69 : pieceSize * 0.78
|
||||
ctx.fillStyle = 'black'
|
||||
ctx.fillText(info, x + xpad, y + ypad)
|
||||
}
|
||||
})
|
||||
}, [
|
||||
cacheMap,
|
||||
@@ -107,6 +122,7 @@ const TorrentCache = ({ cache, isMini }) => {
|
||||
completeColor,
|
||||
readerColor,
|
||||
rangeColor,
|
||||
isMini,
|
||||
theme,
|
||||
])
|
||||
|
||||
|
||||
@@ -4,14 +4,14 @@ import { mainColors } from 'style/colors'
|
||||
export const snakeSettings = {
|
||||
dark: {
|
||||
default: {
|
||||
borderWidth: 2,
|
||||
borderWidth: 1,
|
||||
pieceSize: 14,
|
||||
gapBetweenPieces: 3,
|
||||
borderColor: mainColors.dark.secondary,
|
||||
completeColor: rgba(mainColors.dark.primary, 0.65),
|
||||
borderColor: rgba('#949ca0', 0.25),
|
||||
completeColor: rgba(mainColors.dark.primary, 0.5),
|
||||
backgroundColor: '#f1eff3',
|
||||
progressColor: mainColors.dark.secondary,
|
||||
readerColor: '#000',
|
||||
readerColor: '#8f0405',
|
||||
rangeColor: '#cda184',
|
||||
},
|
||||
mini: {
|
||||
@@ -19,11 +19,11 @@ export const snakeSettings = {
|
||||
borderWidth: 2,
|
||||
pieceSize: 23,
|
||||
gapBetweenPieces: 6,
|
||||
borderColor: '#545a5e',
|
||||
completeColor: '#545a5e',
|
||||
backgroundColor: '#dee3e5',
|
||||
progressColor: '#dee3e5',
|
||||
readerColor: '#000',
|
||||
borderColor: '#5c6469',
|
||||
completeColor: '#5c6469',
|
||||
backgroundColor: '#949ca0',
|
||||
progressColor: '#949ca0',
|
||||
readerColor: '#ccc',
|
||||
rangeColor: '#cda184',
|
||||
},
|
||||
},
|
||||
@@ -48,7 +48,7 @@ export const snakeSettings = {
|
||||
completeColor: '#4db380',
|
||||
backgroundColor: '#dbf2e8',
|
||||
progressColor: '#dbf2e8',
|
||||
readerColor: '#2d714f',
|
||||
readerColor: '#0a0a0a',
|
||||
rangeColor: '#afa6e3',
|
||||
},
|
||||
},
|
||||
|
||||
@@ -43,9 +43,9 @@ export const useCreateCacheMap = cache => {
|
||||
const map = []
|
||||
|
||||
for (let i = 0; i < PiecesCount; i++) {
|
||||
const { Size, Length } = Pieces[i] || {}
|
||||
const { Size, Length, Priority } = Pieces[i] || {}
|
||||
|
||||
const newPiece = { id: i, percentage: (Size / Length) * 100 || 0 }
|
||||
const newPiece = { id: i, percentage: (Size / Length) * 100 || 0, priority: Priority || 0 }
|
||||
|
||||
Readers.forEach(r => {
|
||||
if (i === r.Reader) newPiece.isReader = true
|
||||
|
||||
@@ -204,6 +204,7 @@ export default function DialogTorrentDetailsContent({ closeDialog, torrent }) {
|
||||
{bufferSize <= 33554432 && <SectionSubName>{t('BufferNote')}</SectionSubName>}
|
||||
<LoadingProgress
|
||||
value={Filled}
|
||||
style={{ marginTop: '5px' }}
|
||||
fullAmount={bufferSize}
|
||||
label={`${humanizeSize(bufferSize)} / ${humanizeSize(Filled) || `0 ${t('B')}`}`}
|
||||
/>
|
||||
|
||||
@@ -129,12 +129,15 @@ export const SectionSubName = styled.div`
|
||||
},
|
||||
}) => css`
|
||||
${({ mb }) => css`
|
||||
${mb && `margin-top: ${mb / 3}px`};
|
||||
${mb && `margin-bottom: ${mb}px`};
|
||||
line-height: 1.2;
|
||||
color: ${subNameFontColor};
|
||||
|
||||
@media (max-width: 800px) {
|
||||
${mb && `margin-top: ${mb / 4}px`};
|
||||
${mb && `margin-bottom: ${mb / 2}px`};
|
||||
font-size: 11px;
|
||||
font-size: 14px;
|
||||
}
|
||||
`}
|
||||
`}
|
||||
@@ -149,14 +152,15 @@ export const SectionTitle = styled.div`
|
||||
}) => css`
|
||||
${({ mb }) => css`
|
||||
${mb && `margin-bottom: ${mb}px`};
|
||||
font-size: 35px;
|
||||
font-size: 34px;
|
||||
font-weight: 300;
|
||||
line-height: 1;
|
||||
word-break: break-word;
|
||||
color: ${color || titleFontColor};
|
||||
|
||||
@media (max-width: 800px) {
|
||||
font-size: 25px;
|
||||
font-size: 24px;
|
||||
line-height: 1.1;
|
||||
${mb && `margin-bottom: ${mb / 2}px`};
|
||||
}
|
||||
`}
|
||||
@@ -258,15 +262,16 @@ export const WidgetFieldValue = styled.div`
|
||||
},
|
||||
}) => css`
|
||||
grid-area: value;
|
||||
padding: 0 20px;
|
||||
font-size: 24px;
|
||||
padding: 0 20px 0 0;
|
||||
color: ${widgetFontColor};
|
||||
font-size: 25px;
|
||||
background: ${bgColor};
|
||||
border-radius: 0 5px 5px 0;
|
||||
white-space: nowrap;
|
||||
|
||||
@media (max-width: 800px) {
|
||||
font-size: 18px;
|
||||
padding: 0 4px;
|
||||
padding: 0 16px 0 0;
|
||||
}
|
||||
`}
|
||||
`
|
||||
@@ -276,7 +281,7 @@ export const LoadingProgress = styled.div.attrs(
|
||||
value,
|
||||
fullAmount,
|
||||
theme: {
|
||||
dialogTorrentDetailsContent: { gradientEndColor },
|
||||
dialogTorrentDetailsContent: { gradientStartColor, gradientEndColor },
|
||||
},
|
||||
}) => {
|
||||
const percentage = Math.min(100, (value * 100) / fullAmount)
|
||||
@@ -284,7 +289,7 @@ export const LoadingProgress = styled.div.attrs(
|
||||
return {
|
||||
// this block is here according to styled-components recomendation about fast changable components
|
||||
style: {
|
||||
background: `linear-gradient(to right, ${gradientEndColor} 0%, ${gradientEndColor} ${percentage}%, #fff ${percentage}%, #fff 100%)`,
|
||||
background: `linear-gradient(to right, ${gradientStartColor} 0%, ${gradientEndColor} ${percentage}%, #fff ${percentage}%, #fff 100%)`,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
@@ -50,13 +50,13 @@ export default function PrimarySettingsComponent({
|
||||
|
||||
<PreloadCachePercentage
|
||||
value={100 - cachePercentage}
|
||||
label={`${t('Cache')} ${cacheSize} MB`}
|
||||
label={`${t('Cache')} ${cacheSize} ${t('MB')}`}
|
||||
preloadCachePercentage={preloadCachePercentage}
|
||||
/>
|
||||
|
||||
<PreloadCacheValue color={cacheBeforeReaderColor}>
|
||||
<div>
|
||||
{100 - cachePercentage}% ({Math.round((cacheSize / 100) * (100 - cachePercentage))} MB)
|
||||
{100 - cachePercentage}% ({Math.round((cacheSize / 100) * (100 - cachePercentage))} {t('MB')})
|
||||
</div>
|
||||
|
||||
<div>{t('SettingsDialog.CacheBeforeReaderDesc')}</div>
|
||||
@@ -64,7 +64,7 @@ export default function PrimarySettingsComponent({
|
||||
|
||||
<PreloadCacheValue color={cacheAfterReaderColor}>
|
||||
<div>
|
||||
{cachePercentage}% ({Math.round((cacheSize / 100) * cachePercentage)} MB)
|
||||
{cachePercentage}% ({Math.round((cacheSize / 100) * cachePercentage)} {t('MB')})
|
||||
</div>
|
||||
|
||||
<div>{t('SettingsDialog.CacheAfterReaderDesc')}</div>
|
||||
@@ -80,9 +80,9 @@ export default function PrimarySettingsComponent({
|
||||
sliderMin={32}
|
||||
sliderMax={1024}
|
||||
inputMin={32}
|
||||
inputMax={20000}
|
||||
step={8}
|
||||
onBlurCallback={value => setCacheSize(Math.round(value / 8) * 8)}
|
||||
inputMax={999999}
|
||||
step={4}
|
||||
onBlurCallback={value => setCacheSize(Math.round(value / 4) * 4)}
|
||||
/>
|
||||
|
||||
<SliderInput
|
||||
@@ -98,7 +98,7 @@ export default function PrimarySettingsComponent({
|
||||
|
||||
<SliderInput
|
||||
isProMode={isProMode}
|
||||
title={`${t('SettingsDialog.PreloadCache')} - ${preloadCachePercentage}% (${preloadCacheSize} MB)`}
|
||||
title={`${t('SettingsDialog.PreloadCache')} - ${preloadCachePercentage}% (${preloadCacheSize} ${t('MB')})`}
|
||||
value={preloadCachePercentage}
|
||||
setValue={setPreloadCachePercentage}
|
||||
sliderMin={0}
|
||||
|
||||
@@ -85,16 +85,6 @@ export default function SecondarySettingsComponent({ settings, inputForm }) {
|
||||
label={t('SettingsDialog.DHT')}
|
||||
labelPlacement='start'
|
||||
/>
|
||||
<TextField
|
||||
onChange={inputForm}
|
||||
margin='normal'
|
||||
id='DhtConnectionLimit'
|
||||
label={t('SettingsDialog.DhtConnectionLimit')}
|
||||
value={DhtConnectionLimit}
|
||||
type='number'
|
||||
variant='outlined'
|
||||
fullWidth
|
||||
/>
|
||||
<br />
|
||||
<TextField
|
||||
onChange={inputForm}
|
||||
@@ -134,6 +124,7 @@ export default function SecondarySettingsComponent({ settings, inputForm }) {
|
||||
margin='normal'
|
||||
id='PeersListenPort'
|
||||
label={t('SettingsDialog.PeersListenPort')}
|
||||
helperText={t('SettingsDialog.PeersListenPortHint')}
|
||||
value={PeersListenPort}
|
||||
type='number'
|
||||
variant='outlined'
|
||||
|
||||
@@ -129,7 +129,7 @@ const Torrent = ({ torrent }) => {
|
||||
<Dialog open={isDeleteTorrentOpened} onClose={closeDeleteTorrentAlert}>
|
||||
<DialogTitle>{t('DeleteTorrent?')}</DialogTitle>
|
||||
<DialogActions>
|
||||
<Button variant='outlined' onClick={closeDeleteTorrentAlert} color='primary'>
|
||||
<Button variant='outlined' onClick={closeDeleteTorrentAlert} color='secondary'>
|
||||
{t('Cancel')}
|
||||
</Button>
|
||||
|
||||
@@ -139,7 +139,7 @@ const Torrent = ({ torrent }) => {
|
||||
deleteTorrent(torrent)
|
||||
closeDeleteTorrentAlert()
|
||||
}}
|
||||
color='primary'
|
||||
color='secondary'
|
||||
autoFocus
|
||||
>
|
||||
{t('OK')}
|
||||
|
||||
@@ -14,11 +14,11 @@
|
||||
"HashExists": "This torrent is already in database",
|
||||
"OriginalTorrentTitle": "Original torrent title",
|
||||
"TitleBlank": "Title (blank for orig. torrent title)",
|
||||
"TorrentSourceLink": "Torrent source link",
|
||||
"TorrentSourceLink": "Torrent source",
|
||||
"TorrentSourceOptions": "magnet / hash / .torrent file link",
|
||||
"WrongTorrentSource": "Wrong torrent source"
|
||||
},
|
||||
"AddFromLink": "Add from Link",
|
||||
"AddFromLink": "Add Torrent",
|
||||
"AddNewTorrent": "Add new torrent",
|
||||
"B": "B",
|
||||
"bps": "bps",
|
||||
@@ -100,6 +100,7 @@
|
||||
"DownloadRateLimit": "Download Rate Limit",
|
||||
"ForceEncrypt": "Force Encrypt Headers",
|
||||
"PeersListenPort": "Peers Listen Port",
|
||||
"PeersListenPortHint": "1024 - 65535, 0 - auto",
|
||||
"PreloadCache": "Preload Cache Before Play",
|
||||
"ProMode": "PRO mode",
|
||||
"RAM": "RAM",
|
||||
|
||||
@@ -9,11 +9,11 @@
|
||||
"Or": "ИЛИ",
|
||||
"ClickOrDrag": "НАЖМИТЕ / ПЕРЕТАЩИТЕ ФАЙЛ (.torrent)"
|
||||
},
|
||||
"CustomTorrentTitle": "Cвое имя (не обязательно)",
|
||||
"CustomTorrentTitle": "Cвое название (не обязательно)",
|
||||
"CustomTorrentTitleHelperText": "Напишите свое название, чтобы найти постер",
|
||||
"HashExists": "Этот торрент уже есть в базе данных",
|
||||
"OriginalTorrentTitle": "Оригинальное имя торрента",
|
||||
"TitleBlank": "Имя (пустое - ориг. имя торрента)",
|
||||
"OriginalTorrentTitle": "Оригинальное название торрента",
|
||||
"TitleBlank": "Название (пустое - ориг. название торрента)",
|
||||
"TorrentSourceLink": "Ссылка на источник торрента",
|
||||
"TorrentSourceOptions": "magnet-ссылка / хеш / ссылка на .torrent файл",
|
||||
"WrongTorrentSource": "Неправильный torrent-источник"
|
||||
@@ -100,6 +100,7 @@
|
||||
"DownloadRateLimit": "Ограничение скорости загрузки",
|
||||
"ForceEncrypt": "Принудительное шифрование заголовков",
|
||||
"PeersListenPort": "Порт для входящих подключений",
|
||||
"PeersListenPortHint": "1024 - 65535, 0 - авто",
|
||||
"PreloadCache": "Буфер предзагрузки",
|
||||
"ProMode": "ПРО-режим",
|
||||
"RAM": "Оперативная память",
|
||||
|
||||
Reference in New Issue
Block a user