merge maser

This commit is contained in:
nikk gitanes
2021-08-20 08:10:17 +03:00
41 changed files with 353 additions and 135 deletions

View File

@@ -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

View File

@@ -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/*

View File

@@ -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)
}
}

View File

@@ -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")
}
}
}()

View File

@@ -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
)

View File

@@ -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)
}
}

View File

@@ -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() {

View File

@@ -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 {

View File

@@ -209,6 +209,7 @@ func SetDefSettings() {
func Shutdown() {
bts.Disconnect()
sets.CloseDB()
log.TLogln("Received shutdown. Quit")
os.Exit(0)
}

View File

@@ -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 {

View File

@@ -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()

View File

@@ -20,6 +20,7 @@ type ItemState struct {
Length int64
Size int64
Completed bool
Priority int
}
type ReaderState struct {

View File

@@ -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
}
}

View File

@@ -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()
}
}

View File

@@ -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 {

View File

@@ -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
}

View File

@@ -1,3 +1,3 @@
package version
const Version = "MatriX.102.OE"
const Version = "MatriX.103.OE"

View File

@@ -71,7 +71,7 @@ func addTorrent(req torrReqJS, c *gin.Context) {
req.Link = strings.ReplaceAll(req.Link, "&amp;", "&")
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

View File

@@ -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

View File

@@ -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)
}

BIN
upx Executable file

Binary file not shown.

View File

@@ -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);
}
}
`

View File

@@ -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>

View File

@@ -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>
),

View File

@@ -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 } = {}) => {

View File

@@ -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>
)

View File

@@ -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,
])

View File

@@ -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',
},
},

View File

@@ -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

View File

@@ -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')}`}`}
/>

View File

@@ -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%)`,
},
}
},

View File

@@ -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}

View File

@@ -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'

View File

@@ -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')}

View File

@@ -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",

View File

@@ -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": "Оперативная память",