diff --git a/README.md b/README.md index f6764e2..4a1adc4 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,8 @@ On linux systems you need to set the environment variable before run \ Install golang 1.16+ by instruction: https://golang.org/doc/install \ Goto dir to source\ Run build script under linux build-all.sh\ -For build web page need install npm\ +For build web page need install npm and yarn\ +For instal yarn: _npm i -g yarn_ after install npm\ For build android server need android toolchain\ Download android ndk and copy android-ndk-XXX/toolchains/llvm/prebuilt/linux-x86_64 dir to source, rename it to toolchain @@ -75,6 +76,11 @@ TorrServer [--port PORT] [--path PATH] [--logpath LOGPATH] [--rdb] [--httpauth] > >http://127.0.0.1:8090/stream/fname?link=...&save&title=...&poster=... +###### /play/:hash/:id +#### params: +* hash - hash of torrent +* index - index of file + ###### /playlistall/all.m3u *Get all http links of all torrents in m3u list* @@ -180,7 +186,7 @@ local:127.0.0.1\ [PayPal](https://www.paypal.me/yourok) [YooMoney](https://yoomoney.ru/to/410013733697114/200) -YooMoney card: 5599 0050 6424 4747 +YooMoney card: 4048 4150 1812 8179 SberBank card: 4276 4000 6707 2919 @@ -192,6 +198,8 @@ SberBank card: 4276 4000 6707 2919 ###### **tsynik** [github.com/tsynik](https://github.com/tsynik) +###### **dancheskus** [github.com/dancheskus](https://github.com/dancheskus) + ###### **Tw1cker Руслан Пахнев** [github.com/Nemiroff](https://github.com/Nemiroff) ###### **SpAwN_LMG** diff --git a/build-all.sh b/build-all.sh index 1df528a..39fae51 100755 --- a/build-all.sh +++ b/build-all.sh @@ -34,6 +34,7 @@ OUTPUT="${ROOT}/dist/TorrServer" #### Build web echo "Build web" cd "${ROOT}/web" || exit 1 +npm install --silent npm run --silent build-js cp "${ROOT}/web/dest/index.html" "${ROOT}/server/web/pages/template/pages/" @@ -55,6 +56,7 @@ for PLATFORM in "${PLATFORMS[@]}"; do set_goarm "$GOARCH" BIN_FILENAME="${OUTPUT}-${GOOS}-${GOARCH}${GOARM}" if [[ "${GOOS}" == "windows" ]]; then BIN_FILENAME="${BIN_FILENAME}.exe"; fi + # CMD="GOOS=${GOOS} GOARCH=${GOARCH} ${GO_ARM} CGO_ENABLED=0 ${GOBIN} build ${BUILD_FLAGS} -o ${BIN_FILENAME} ./cmd" CMD="GOOS=${GOOS} GOARCH=${GOARCH} ${GO_ARM} ${GOBIN} build ${BUILD_FLAGS} -o ${BIN_FILENAME} ./cmd" echo "${CMD}" eval "$CMD" || FAILURES="${FAILURES} ${GOOS}/${GOARCH}${GOARM}" diff --git a/docker-all.sh b/docker-all.sh new file mode 100755 index 0000000..b2817c1 --- /dev/null +++ b/docker-all.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +ROOT=${PWD} + +#### Build web +echo "Build web" +cd "${ROOT}/web" || exit 1 +npm install --silent +npm run --silent build-js +cp "${ROOT}/web/dest/index.html" "${ROOT}/server/web/pages/template/pages/" +cd .. + +sudo docker run --rm -v "$PWD":/usr/src/torr -w /usr/src/torr golang:1.16 ./build-all.sh +sudo chmod 0777 ./dist/* \ No newline at end of file diff --git a/server/settings/btsets.go b/server/settings/btsets.go index a21e2c0..6a167c1 100644 --- a/server/settings/btsets.go +++ b/server/settings/btsets.go @@ -8,11 +8,14 @@ import ( type BTSets struct { // Cache - CacheSize int64 // in byte, def 200 mb - PreloadBuffer bool - ReaderReadAHead int // in percent, 5%-100%, [...S__X__E...] [S-E] not clean - UseDisk bool - TorrentsSavePath string + CacheSize int64 // in byte, def 200 mb + PreloadBuffer bool + ReaderReadAHead int // in percent, 5%-100%, [...S__X__E...] [S-E] not clean + + // Disk + UseDisk bool + TorrentsSavePath string + RemoveCacheOnDrop bool // Torrent ForceEncrypt bool @@ -56,6 +59,11 @@ func SetBTSets(sets *BTSets) { if sets.ReaderReadAHead > 100 { sets.ReaderReadAHead = 100 } + + if sets.TorrentsSavePath == "" { + sets.UseDisk = false + } + BTsets = sets buf, err := json.Marshal(BTsets) if err != nil { diff --git a/server/torr/apihelper.go b/server/torr/apihelper.go index cadff77..5229045 100644 --- a/server/torr/apihelper.go +++ b/server/torr/apihelper.go @@ -2,6 +2,7 @@ package torr import ( "io" + "io/ioutil" "os" "path/filepath" "sort" @@ -125,22 +126,19 @@ func SetTorrent(hashHex, title, poster, data string) *Torrent { func RemTorrent(hashHex string) { hash := metainfo.NewHashFromHex(hashHex) - bts.RemoveTorrent(hash) - RemTorrentDB(hash) - if sets.BTsets.UseDisk && - hashHex != "" && - hashHex != "/" && - sets.BTsets.TorrentsSavePath != "" && - sets.BTsets.TorrentsSavePath != "/" { - + if sets.BTsets.UseDisk && hashHex != "" && hashHex != "/" { name := filepath.Join(sets.BTsets.TorrentsSavePath, hashHex) - err := os.RemoveAll(name) + ff, _ := ioutil.ReadDir(name) + for _, f := range ff { + os.Remove(filepath.Join(name, f.Name())) + } + err := os.Remove(name) if err != nil { log.TLogln("Error remove cache:", err) - } else { - log.TLogln("Remove cache from disk:", hashHex) } } + bts.RemoveTorrent(hash) + RemTorrentDB(hash) } func ListTorrent() []*Torrent { diff --git a/server/torr/preload.go b/server/torr/preload.go new file mode 100644 index 0000000..c41bb59 --- /dev/null +++ b/server/torr/preload.go @@ -0,0 +1,154 @@ +package torr + +import ( + "fmt" + "io" + "sync" + "time" + + "github.com/anacrolix/torrent" + + "server/log" + "server/settings" + "server/torr/state" + utils2 "server/utils" +) + +func (t *Torrent) Preload(index int, size int64) { + if size <= 0 { + return + } + t.PreloadSize = size + + if t.Stat == state.TorrentGettingInfo { + if !t.WaitInfo() { + return + } + // wait change status + time.Sleep(100 * time.Millisecond) + } + + t.muTorrent.Lock() + if t.Stat != state.TorrentWorking { + t.muTorrent.Unlock() + return + } + + t.Stat = state.TorrentPreload + t.muTorrent.Unlock() + + defer func() { + if t.Stat == state.TorrentPreload { + t.Stat = state.TorrentWorking + } + }() + + file := t.findFileIndex(index) + if file == nil { + file = t.Files()[0] + } + + if size > file.Length() { + size = file.Length() + } + + if t.Info() != nil { + // Запуск лога в отдельном потоке + go func() { + for t.Stat == state.TorrentPreload { + stat := fmt.Sprint(file.Torrent().InfoHash().HexString(), " ", utils2.Format(float64(t.PreloadedBytes)), "/", utils2.Format(float64(t.PreloadSize)), " Speed:", utils2.Format(t.DownloadSpeed), " Peers:[", t.Torrent.Stats().ConnectedSeeders, "]", t.Torrent.Stats().ActivePeers, "/", t.Torrent.Stats().TotalPeers) + log.TLogln("Preload:", stat) + t.AddExpiredTime(time.Second * time.Duration(settings.BTsets.TorrentDisconnectTimeout)) + time.Sleep(time.Second) + } + }() + + mb5 := int64(5 * 1024 * 1024) + + readerStart := file.NewReader() + defer readerStart.Close() + readerStart.SetResponsive() + readerStart.SetReadahead(0) + readerStartEnd := size - mb5 + + if readerStartEnd < 0 { + // Если конец начального ридера оказался за началом + readerStartEnd = size + } + if readerStartEnd > file.Length() { + // Если конец начального ридера оказался после конца файла + readerStartEnd = file.Length() + } + + readerEndStart := file.Length() - mb5 + readerEndEnd := file.Length() + + var wa sync.WaitGroup + go func() { + offset := int64(0) + if readerEndStart > readerStartEnd { + // Если конечный ридер не входит в диапозон начального + wa.Add(1) + defer wa.Done() + readerEnd := file.NewReader() + readerEnd.SetResponsive() + readerEnd.SetReadahead(0) + readerEnd.Seek(readerEndStart, io.SeekStart) + offset = readerEndStart + tmp := make([]byte, 32768, 32768) + for offset+int64(len(tmp)) < readerEndEnd { + n, err := readerEnd.Read(tmp) + if err != nil { + break + } + offset += int64(n) + } + readerEnd.Close() + } + }() + + pieceLength := t.Info().PieceLength + readahead := pieceLength * 4 + if readerStartEnd < readahead { + readahead = 0 + } + readerStart.SetReadahead(readahead) + offset := int64(0) + tmp := make([]byte, 32768, 32768) + for offset+int64(len(tmp)) < readerStartEnd { + n, err := readerStart.Read(tmp) + if err != nil { + log.TLogln("Error preload:", err) + return + } + offset += int64(n) + if readahead > 0 && readerStartEnd-(offset+int64(len(tmp))) < readahead { + readahead = 0 + readerStart.SetReadahead(0) + } + } + + wa.Wait() + } + log.TLogln("End preload:", file.Torrent().InfoHash().HexString(), "Peers:[", t.Torrent.Stats().ConnectedSeeders, "]", t.Torrent.Stats().ActivePeers, "/", t.Torrent.Stats().TotalPeers) +} + +func (t *Torrent) findFileIndex(index int) *torrent.File { + st := t.Status() + var stFile *state.TorrentFileStat + for _, f := range st.FileStats { + if index == f.Id { + stFile = f + break + } + } + if stFile == nil { + return nil + } + for _, file := range t.Files() { + if file.Path() == stFile.Path { + return file + } + } + return nil +} diff --git a/server/torr/storage/torrstor/cache.go b/server/torr/storage/torrstor/cache.go index 3266aa3..a2258d5 100644 --- a/server/torr/storage/torrstor/cache.go +++ b/server/torr/storage/torrstor/cache.go @@ -1,11 +1,9 @@ package torrstor import ( - "io/ioutil" "os" "path/filepath" "sort" - "strconv" "sync" "time" @@ -76,21 +74,6 @@ func (c *Cache) Init(info *metainfo.Info, hash metainfo.Hash) { for i := 0; i < c.pieceCount; i++ { c.pieces[i] = NewPiece(i, c) } - - if settings.BTsets.UseDisk { - name := filepath.Join(settings.BTsets.TorrentsSavePath, hash.HexString()) - fs, err := ioutil.ReadDir(name) - if err == nil { - for _, f := range fs { - id, err := strconv.Atoi(f.Name()) - if err == nil { - c.pieces[id].Size = f.Size() - c.pieces[id].Complete = f.Size() == c.pieceLength - c.pieces[id].Accessed = f.ModTime().Unix() - } - } - } - } } func (c *Cache) SetTorrent(torr *torrent.Torrent) { @@ -107,6 +90,19 @@ func (c *Cache) Piece(m metainfo.Piece) storage.PieceImpl { func (c *Cache) Close() error { log.TLogln("Close cache for:", c.hash) delete(c.storage.caches, c.hash) + + if settings.BTsets.RemoveCacheOnDrop { + name := filepath.Join(settings.BTsets.TorrentsSavePath, c.hash.HexString()) + if name != "" && name != "/" { + for _, v := range c.pieces { + if v.dPiece != nil { + os.Remove(v.dPiece.name) + } + } + os.Remove(name) + } + } + c.pieces = nil c.muReaders.Lock() @@ -137,28 +133,33 @@ func (c *Cache) GetState() *state.CacheState { piecesState := make(map[int]state.ItemState, 0) var fill int64 = 0 - for _, p := range c.pieces { - if p.Size > 0 { - fill += p.Size - piecesState[p.Id] = state.ItemState{ - Id: p.Id, - Size: p.Size, - Length: c.pieceLength, - Completed: p.Complete, + + if len(c.pieces) > 0 { + for _, p := range c.pieces { + if p.Size > 0 { + fill += p.Size + piecesState[p.Id] = state.ItemState{ + Id: p.Id, + Size: p.Size, + Length: c.pieceLength, + Completed: p.Complete, + } } } } readersState := make([]*state.ReaderState, 0) c.muReaders.Lock() - for r, _ := range c.readers { - rng := r.getPiecesRange() - pc := r.getReaderPiece() - readersState = append(readersState, &state.ReaderState{ - Start: rng.Start, - End: rng.End, - Reader: pc, - }) + if len(c.readers) > 0 { + for r, _ := range c.readers { + rng := r.getPiecesRange() + pc := r.getReaderPiece() + readersState = append(readersState, &state.ReaderState{ + Start: rng.Start, + End: rng.End, + Reader: pc, + }) + } } c.muReaders.Unlock() @@ -224,6 +225,12 @@ func (c *Cache) getRemPieces() []*Piece { piecesRemove = append(piecesRemove, p) } } + } else { + // on preload clean + //TODO проверить + if p.Size > 0 && !c.isIdInFileBE(ranges, id) { + piecesRemove = append(piecesRemove, p) + } } } @@ -269,68 +276,6 @@ func (c *Cache) isIdInFileBE(ranges []Range, id int) bool { return false } -// run only in cache on disk -func (c *Cache) LoadPiecesOnDisk() { - if c.torrent == nil { - return - } - - if c.isRemove { - return - } - c.muRemove.Lock() - if c.isRemove { - c.muRemove.Unlock() - return - } - c.isRemove = true - defer func() { c.isRemove = false }() - c.muRemove.Unlock() - - ranges := make([]Range, 0) - c.muReaders.Lock() - for r, _ := range c.readers { - ranges = append(ranges, r.getPiecesRange()) - } - c.muReaders.Unlock() - ranges = mergeRange(ranges) - - for r, _ := range c.readers { - pc := r.getReaderPiece() - limit := 5 - - for limit > 0 { - if !c.pieces[pc].Complete { - if c.torrent.PieceState(pc).Priority == torrent.PiecePriorityNone { - c.torrent.Piece(pc).SetPriority(torrent.PiecePriorityNormal) - } - limit-- - } - pc++ - } - } - if len(c.readers) == 0 { - limit := 5 - pc := 0 - end := c.pieceCount - for pc <= end { - if !c.pieces[pc].Complete { - break - } - pc++ - } - 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) - } - limit-- - } - pc++ - } - } -} - ////////////////// // Reader section //////// diff --git a/server/torr/storage/torrstor/diskpiece.go b/server/torr/storage/torrstor/diskpiece.go index 71bc569..4046114 100644 --- a/server/torr/storage/torrstor/diskpiece.go +++ b/server/torr/storage/torrstor/diskpiece.go @@ -1,12 +1,15 @@ package torrstor import ( + "io" "os" "path/filepath" "strconv" "sync" "time" + "github.com/anacrolix/torrent" + "server/log" "server/settings" ) @@ -14,30 +17,38 @@ import ( type DiskPiece struct { piece *Piece - file *os.File + name string mu sync.RWMutex } func NewDiskPiece(p *Piece) *DiskPiece { name := filepath.Join(settings.BTsets.TorrentsSavePath, p.cache.hash.HexString(), strconv.Itoa(p.Id)) - ff, err := os.OpenFile(name, os.O_RDWR|os.O_CREATE, 0666) - if err != nil { - log.TLogln("Error open file:", err) - return nil + ff, err := os.Stat(name) + if err == nil { + p.Size = ff.Size() + p.Complete = ff.Size() == p.cache.pieceLength + p.Accessed = ff.ModTime().Unix() } - return &DiskPiece{piece: p, file: ff} + return &DiskPiece{piece: p, name: name} } func (p *DiskPiece) WriteAt(b []byte, off int64) (n int, err error) { p.mu.Lock() defer p.mu.Unlock() - n, err = p.file.WriteAt(b, off) - - go p.piece.cache.LoadPiecesOnDisk() + ff, err := os.OpenFile(p.name, os.O_RDWR|os.O_CREATE, 0666) + if err != nil { + log.TLogln("Error open file:", err) + return 0, err + } + defer ff.Close() + n, err = ff.WriteAt(b, off) p.piece.Size += int64(n) + if p.piece.Size > p.piece.cache.pieceLength { + p.piece.Size = p.piece.cache.pieceLength + } p.piece.Accessed = time.Now().Unix() return } @@ -46,12 +57,31 @@ func (p *DiskPiece) ReadAt(b []byte, off int64) (n int, err error) { p.mu.Lock() defer p.mu.Unlock() - n, err = p.file.ReadAt(b, off) + ff, err := os.OpenFile(p.name, os.O_RDONLY, 0666) + if os.IsNotExist(err) { + return 0, io.EOF + } + if err != nil { + log.TLogln("Error open file:", err) + return 0, err + } + defer ff.Close() + + n, err = ff.ReadAt(b, off) p.piece.Accessed = time.Now().Unix() + if int64(len(b))+off >= p.piece.Size { + go p.piece.cache.cleanPieces() + } return n, nil } func (p *DiskPiece) Release() { - p.file.Close() + p.mu.Lock() + defer p.mu.Unlock() + + p.piece.Size = 0 + p.piece.Complete = false + + p.piece.cache.torrent.Piece(p.piece.Id).SetPriority(torrent.PiecePriorityNone) } diff --git a/server/torr/storage/torrstor/mempiece.go b/server/torr/storage/torrstor/mempiece.go index 3f7b48c..6b69724 100644 --- a/server/torr/storage/torrstor/mempiece.go +++ b/server/torr/storage/torrstor/mempiece.go @@ -29,6 +29,9 @@ func (p *MemPiece) WriteAt(b []byte, off int64) (n int, err error) { } n = copy(p.buffer[off:], b[:]) p.piece.Size += int64(n) + if p.piece.Size > p.piece.cache.pieceLength { + p.piece.Size = p.piece.cache.pieceLength + } p.piece.Accessed = time.Now().Unix() return } diff --git a/server/torr/storage/torrstor/reader.go b/server/torr/storage/torrstor/reader.go index 2cbf3dc..3d5e57e 100644 --- a/server/torr/storage/torrstor/reader.go +++ b/server/torr/storage/torrstor/reader.go @@ -135,7 +135,7 @@ func (r *Reader) getPieceNum(offset int64) int { func (r *Reader) getOffsetRange() (int64, int64) { - if time.Now().Unix() > r.lastAccess+60 { + if time.Now().Unix() > r.lastAccess+60 && len(r.cache.readers) > 1 { return r.file.Offset(), r.file.Offset() } diff --git a/server/torr/torrent.go b/server/torr/torrent.go index 59494fd..b7167db 100644 --- a/server/torr/torrent.go +++ b/server/torr/torrent.go @@ -2,21 +2,19 @@ package torr import ( "errors" - "fmt" "sort" "sync" "time" + "github.com/anacrolix/torrent" + "github.com/anacrolix/torrent/metainfo" + "server/log" "server/settings" "server/torr/state" cacheSt "server/torr/storage/state" "server/torr/storage/torrstor" "server/torr/utils" - utils2 "server/utils" - - "github.com/anacrolix/torrent" - "github.com/anacrolix/torrent/metainfo" ) type Torrent struct { @@ -109,7 +107,6 @@ func (t *Torrent) WaitInfo() bool { case <-t.Torrent.GotInfo(): t.cache = t.bt.storage.GetCache(t.Hash()) t.cache.SetTorrent(t.Torrent) - go t.cache.LoadPiecesOnDisk() return true case <-t.closed: return false @@ -248,105 +245,6 @@ func (t *Torrent) GetCache() *torrstor.Cache { return t.cache } -func (t *Torrent) Preload(index int, size int64) { - if size <= 0 { - return - } - t.PreloadSize = size - - if t.Stat == state.TorrentGettingInfo { - if !t.WaitInfo() { - return - } - // wait change status - time.Sleep(100 * time.Millisecond) - } - - t.muTorrent.Lock() - if t.Stat != state.TorrentWorking { - t.muTorrent.Unlock() - return - } - - t.Stat = state.TorrentPreload - t.muTorrent.Unlock() - - defer func() { - if t.Stat == state.TorrentPreload { - t.Stat = state.TorrentWorking - } - }() - - file := t.findFileIndex(index) - if file == nil { - file = t.Files()[0] - } - - if size > file.Length() { - size = file.Length() - } - - if t.Info() != nil { - pieceLength := t.Info().PieceLength - mb5 := int64(5 * 1024 * 1024) - - pieceFileStart := int(file.Offset() / pieceLength) - pieceFileEnd := int((file.Offset() + file.Length()) / pieceLength) - readerPieceBefore := int((file.Offset() + size - mb5) / pieceLength) - readerPieceAfter := int((file.Offset() + file.Length() - mb5) / pieceLength) - - lastStat := time.Now().Add(-time.Second) - - for true { - t.muTorrent.Lock() - if t.Torrent == nil { - return - } - - t.PreloadedBytes = t.cache.GetState().Filled - t.muTorrent.Unlock() - - stat := fmt.Sprint(file.Torrent().InfoHash().HexString(), " ", utils2.Format(float64(t.PreloadedBytes)), "/", utils2.Format(float64(t.PreloadSize)), " Speed:", utils2.Format(t.DownloadSpeed), " Peers:[", t.Torrent.Stats().ConnectedSeeders, "]", t.Torrent.Stats().ActivePeers, "/", t.Torrent.Stats().TotalPeers) - if time.Since(lastStat) > time.Second { - log.TLogln("Preload:", stat) - lastStat = time.Now() - } - - isComplete := true - if readerPieceBefore >= pieceFileStart { - for i := pieceFileStart; i < readerPieceBefore; i++ { - if !t.PieceState(i).Complete { - isComplete = false - if t.PieceState(i).Priority == torrent.PiecePriorityNone { - t.Piece(i).SetPriority(torrent.PiecePriorityNormal) - } - } - } - } - if readerPieceAfter <= pieceFileEnd { - for i := readerPieceAfter; i <= pieceFileEnd; i++ { - if !t.PieceState(i).Complete { - isComplete = false - if t.PieceState(i).Priority == torrent.PiecePriorityNone { - t.Piece(i).SetPriority(torrent.PiecePriorityNormal) - } - } - } - } - if t.PreloadedBytes >= size-pieceLength { - isComplete = true - } - - t.AddExpiredTime(time.Second * time.Duration(settings.BTsets.TorrentDisconnectTimeout)) - if isComplete { - break - } - time.Sleep(time.Second) - } - } - log.TLogln("End preload:", file.Torrent().InfoHash().HexString(), "Peers:[", t.Torrent.Stats().ConnectedSeeders, "]", t.Torrent.Stats().ActivePeers, "/", t.Torrent.Stats().TotalPeers) -} - func (t *Torrent) drop() { t.muTorrent.Lock() if t.Torrent != nil { @@ -427,6 +325,7 @@ func (t *Torrent) Status() *state.TorrentStatus { } } } + return st } @@ -438,23 +337,3 @@ func (t *Torrent) CacheState() *cacheSt.CacheState { } return nil } - -func (t *Torrent) findFileIndex(index int) *torrent.File { - st := t.Status() - var stFile *state.TorrentFileStat - for _, f := range st.FileStats { - if index == f.Id { - stFile = f - break - } - } - if stFile == nil { - return nil - } - for _, file := range t.Files() { - if file.Path() == stFile.Path { - return file - } - } - return nil -} diff --git a/server/version/version.go b/server/version/version.go index 5ebb870..8a04198 100644 --- a/server/version/version.go +++ b/server/version/version.go @@ -1,3 +1,3 @@ package version -const Version = "MatriX.93.2_NE" +const Version = "MatriX.97.1" diff --git a/server/web/api/play.go b/server/web/api/play.go new file mode 100644 index 0000000..8540c65 --- /dev/null +++ b/server/web/api/play.go @@ -0,0 +1,73 @@ +package api + +import ( + "errors" + "net/http" + "strconv" + + "github.com/gin-gonic/gin" + + "server/torr" + "server/torr/state" + "server/web/api/utils" +) + +func play(c *gin.Context) { + hash := c.Param("hash") + indexStr := c.Param("id") + notAuth := c.GetBool("not_auth") + + if hash == "" || indexStr == "" { + c.AbortWithError(http.StatusNotFound, errors.New("link should not be empty")) + return + } + + spec, err := utils.ParseLink(hash) + if err != nil { + c.AbortWithError(http.StatusInternalServerError, err) + return + } + + tor := torr.GetTorrent(spec.InfoHash.HexString()) + if tor == nil && notAuth { + c.Header("WWW-Authenticate", "Basic realm=Authorization Required") + c.AbortWithStatus(http.StatusUnauthorized) + return + } + + if tor == nil { + c.AbortWithError(http.StatusInternalServerError, errors.New("error get torrent")) + return + } + + if tor.Stat == state.TorrentInDB { + tor, err = torr.AddTorrent(spec, tor.Title, tor.Poster, tor.Data) + if err != nil { + c.AbortWithError(http.StatusInternalServerError, err) + return + } + } + + if !tor.GotInfo() { + c.AbortWithError(http.StatusInternalServerError, errors.New("timeout connection torrent")) + return + } + + // find file + index := -1 + if len(tor.Files()) == 1 { + index = 1 + } else { + ind, err := strconv.Atoi(indexStr) + if err == nil { + index = ind + } + } + if index == -1 { // if file index not set and play file exec + c.AbortWithError(http.StatusBadRequest, errors.New("\"index\" is wrong")) + return + } + + tor.Stream(index, c.Request, c.Writer) + return +} diff --git a/server/web/api/route.go b/server/web/api/route.go index 270644b..1ad71a2 100644 --- a/server/web/api/route.go +++ b/server/web/api/route.go @@ -30,6 +30,9 @@ func SetupRoute(route *gin.RouterGroup) { route.GET("/stream", stream) route.GET("/stream/*fname", stream) + route.HEAD("/play/:hash/:id", play) + route.GET("/play/:hash/:id", play) + route.POST("/viewed", viewed) route.GET("/playlistall/all.m3u", allPlayList) diff --git a/server/web/api/torrents.go b/server/web/api/torrents.go index 079eef2..b15b0d3 100644 --- a/server/web/api/torrents.go +++ b/server/web/api/torrents.go @@ -136,7 +136,7 @@ func remTorrent(req torrReqJS, c *gin.Context) { func listTorrent(req torrReqJS, c *gin.Context) { list := torr.ListTorrent() - if list == nil { + if len(list) == 0 { c.JSON(200, []*state.TorrentStatus{}) return } diff --git a/server/web/auth/auth.go b/server/web/auth/auth.go index 721956b..bc4d66f 100644 --- a/server/web/auth/auth.go +++ b/server/web/auth/auth.go @@ -64,6 +64,7 @@ func BasicAuth(accounts gin.Accounts) gin.HandlerFunc { user, found := pairs.searchCredential(c.Request.Header.Get("Authorization")) if !found { if strings.HasPrefix(c.FullPath(), "/stream") || + strings.HasPrefix(c.FullPath(), "/play") || (strings.HasPrefix(c.FullPath(), "/playlist") && c.FullPath() != "/playlistall/all.m3u") { c.Set("not_auth", true) return diff --git a/server/web/pages/route.go b/server/web/pages/route.go index c99828c..bc5a2ed 100644 --- a/server/web/pages/route.go +++ b/server/web/pages/route.go @@ -1,7 +1,10 @@ package pages import ( + "github.com/anacrolix/torrent/metainfo" "github.com/gin-gonic/gin" + + "server/settings" "server/torr" "server/web/pages/template" ) @@ -9,6 +12,7 @@ import ( func SetupRoute(route *gin.RouterGroup) { route.GET("/", mainPage) route.GET("/stat", statPage) + route.GET("/magnets", getTorrents) } func mainPage(c *gin.Context) { @@ -19,3 +23,19 @@ func statPage(c *gin.Context) { torr.WriteStatus(c.Writer) c.Status(200) } + +func getTorrents(c *gin.Context) { + list := settings.ListTorrent() + http := "