mirror of
https://github.com/Ernous/TorrServerJellyfin.git
synced 2025-12-19 21:46:11 +05:00
update
This commit is contained in:
@@ -126,9 +126,9 @@ func (v *TDB) List(xpath string) []string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
buckt.ForEach(func(_, v []byte) error {
|
buckt.ForEach(func(k, _ []byte) error {
|
||||||
if len(v) > 0 {
|
if len(k) > 0 {
|
||||||
ret = append(ret, string(v))
|
ret = append(ret, string(k))
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import (
|
|||||||
|
|
||||||
"github.com/anacrolix/torrent"
|
"github.com/anacrolix/torrent"
|
||||||
"github.com/anacrolix/torrent/metainfo"
|
"github.com/anacrolix/torrent/metainfo"
|
||||||
"server/torr"
|
"server/torr/state"
|
||||||
)
|
)
|
||||||
|
|
||||||
type TorrentDB struct {
|
type TorrentDB struct {
|
||||||
@@ -18,7 +18,7 @@ type TorrentDB struct {
|
|||||||
|
|
||||||
Timestamp int64 `json:"timestamp,omitempty"`
|
Timestamp int64 `json:"timestamp,omitempty"`
|
||||||
|
|
||||||
Files []torr.TorrentFileStat `json:"files,omitempty"`
|
Files []state.TorrentFileStat `json:"files,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type File struct {
|
type File struct {
|
||||||
@@ -63,7 +63,7 @@ func ListTorrent() []*TorrentDB {
|
|||||||
buf := tdb.Get("Torrents", key)
|
buf := tdb.Get("Torrents", key)
|
||||||
if len(buf) > 0 {
|
if len(buf) > 0 {
|
||||||
var torr *TorrentDB
|
var torr *TorrentDB
|
||||||
err := json.Unmarshal(buf, torr)
|
err := json.Unmarshal(buf, &torr)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
list = append(list, torr)
|
list = append(list, torr)
|
||||||
}
|
}
|
||||||
|
|||||||
79
src/server/settings/viewed.go
Normal file
79
src/server/settings/viewed.go
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
package settings
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
"server/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Viewed struct {
|
||||||
|
Hash string `json:"hash"`
|
||||||
|
FileIndex int `json:"file_index"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetViewed(vv *Viewed) {
|
||||||
|
buf := tdb.Get("Viewed", vv.Hash)
|
||||||
|
var indeces map[int]struct{}
|
||||||
|
err := json.Unmarshal(buf, &indeces)
|
||||||
|
if err == nil {
|
||||||
|
indeces[vv.FileIndex] = struct{}{}
|
||||||
|
buf, err = json.Marshal(indeces)
|
||||||
|
if err == nil {
|
||||||
|
tdb.Set("Viewed", vv.Hash, buf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
log.TLogln("Error set viewed:", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func RemViewed(vv *Viewed) {
|
||||||
|
buf := tdb.Get("Viewed", vv.Hash)
|
||||||
|
var indeces map[int]struct{}
|
||||||
|
err := json.Unmarshal(buf, &indeces)
|
||||||
|
if err == nil {
|
||||||
|
delete(indeces, vv.FileIndex)
|
||||||
|
buf, err = json.Marshal(indeces)
|
||||||
|
if err == nil {
|
||||||
|
tdb.Set("Viewed", vv.Hash, buf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
log.TLogln("Error rem viewed:", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ListViewed(hash string) []*Viewed {
|
||||||
|
var err error
|
||||||
|
if hash != "" {
|
||||||
|
buf := tdb.Get("Viewed", hash)
|
||||||
|
var indeces map[int]struct{}
|
||||||
|
err = json.Unmarshal(buf, &indeces)
|
||||||
|
if err == nil {
|
||||||
|
var ret []*Viewed
|
||||||
|
for i, _ := range indeces {
|
||||||
|
ret = append(ret, &Viewed{hash, i})
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
var ret []*Viewed
|
||||||
|
keys := tdb.List("Viewed")
|
||||||
|
for _, key := range keys {
|
||||||
|
buf := tdb.Get("Viewed", key)
|
||||||
|
var indeces map[int]struct{}
|
||||||
|
err = json.Unmarshal(buf, &indeces)
|
||||||
|
if err == nil {
|
||||||
|
for i, _ := range indeces {
|
||||||
|
ret = append(ret, &Viewed{hash, i})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.TLogln("Error list viewed:", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -83,6 +83,9 @@ func (bt *BTServer) configure() {
|
|||||||
bt.config.HTTPUserAgent = userAgent
|
bt.config.HTTPUserAgent = userAgent
|
||||||
bt.config.ExtendedHandshakeClientVersion = cliVers
|
bt.config.ExtendedHandshakeClientVersion = cliVers
|
||||||
bt.config.EstablishedConnsPerTorrent = settings.BTsets.ConnectionsLimit
|
bt.config.EstablishedConnsPerTorrent = settings.BTsets.ConnectionsLimit
|
||||||
|
|
||||||
|
bt.config.DefaultRequestStrategy = torrent.RequestStrategyFastest()
|
||||||
|
|
||||||
if settings.BTsets.DhtConnectionLimit > 0 {
|
if settings.BTsets.DhtConnectionLimit > 0 {
|
||||||
bt.config.ConnTracker.SetMaxEntries(settings.BTsets.DhtConnectionLimit)
|
bt.config.ConnTracker.SetMaxEntries(settings.BTsets.DhtConnectionLimit)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,130 +0,0 @@
|
|||||||
package torr
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"sort"
|
|
||||||
|
|
||||||
"github.com/anacrolix/torrent"
|
|
||||||
)
|
|
||||||
|
|
||||||
type BTState struct {
|
|
||||||
LocalPort int
|
|
||||||
PeerID string
|
|
||||||
BannedIPs int
|
|
||||||
DHTs []torrent.DhtServer
|
|
||||||
|
|
||||||
Torrents []*Torrent
|
|
||||||
}
|
|
||||||
|
|
||||||
func (bt *BTServer) BTState() *BTState {
|
|
||||||
bt.mu.Lock()
|
|
||||||
defer bt.mu.Unlock()
|
|
||||||
|
|
||||||
btState := new(BTState)
|
|
||||||
btState.LocalPort = bt.client.LocalPort()
|
|
||||||
btState.PeerID = fmt.Sprintf("%x", bt.client.PeerID())
|
|
||||||
btState.BannedIPs = len(bt.client.BadPeerIPs())
|
|
||||||
btState.DHTs = bt.client.DhtServers()
|
|
||||||
|
|
||||||
for _, t := range bt.torrents {
|
|
||||||
btState.Torrents = append(btState.Torrents, t)
|
|
||||||
}
|
|
||||||
return btState
|
|
||||||
}
|
|
||||||
|
|
||||||
type TorrentStats struct {
|
|
||||||
Name string `json:"name,omitempty"`
|
|
||||||
Hash string `json:"hash,omitempty"`
|
|
||||||
|
|
||||||
TorrentStatus TorrentStatus `json:"torrent_status,omitempty"`
|
|
||||||
TorrentStatusString string `json:"torrent_status_string,omitempty"`
|
|
||||||
|
|
||||||
LoadedSize int64 `json:"loaded_size,omitempty"`
|
|
||||||
TorrentSize int64 `json:"torrent_size,omitempty"`
|
|
||||||
|
|
||||||
PreloadedBytes int64 `json:"preloaded_bytes,omitempty"`
|
|
||||||
PreloadSize int64 `json:"preload_size,omitempty"`
|
|
||||||
|
|
||||||
DownloadSpeed float64 `json:"download_speed,omitempty"`
|
|
||||||
UploadSpeed float64 `json:"upload_speed,omitempty"`
|
|
||||||
|
|
||||||
TotalPeers int `json:"total_peers,omitempty"`
|
|
||||||
PendingPeers int `json:"pending_peers,omitempty"`
|
|
||||||
ActivePeers int `json:"active_peers,omitempty"`
|
|
||||||
ConnectedSeeders int `json:"connected_seeders,omitempty"`
|
|
||||||
HalfOpenPeers int `json:"half_open_peers,omitempty"`
|
|
||||||
|
|
||||||
BytesWritten int64 `json:"bytes_written,omitempty"`
|
|
||||||
BytesWrittenData int64 `json:"bytes_written_data,omitempty"`
|
|
||||||
BytesRead int64 `json:"bytes_read,omitempty"`
|
|
||||||
BytesReadData int64 `json:"bytes_read_data,omitempty"`
|
|
||||||
BytesReadUsefulData int64 `json:"bytes_read_useful_data,omitempty"`
|
|
||||||
ChunksWritten int64 `json:"chunks_written,omitempty"`
|
|
||||||
ChunksRead int64 `json:"chunks_read,omitempty"`
|
|
||||||
ChunksReadUseful int64 `json:"chunks_read_useful,omitempty"`
|
|
||||||
ChunksReadWasted int64 `json:"chunks_read_wasted,omitempty"`
|
|
||||||
PiecesDirtiedGood int64 `json:"pieces_dirtied_good,omitempty"`
|
|
||||||
PiecesDirtiedBad int64 `json:"pieces_dirtied_bad,omitempty"`
|
|
||||||
|
|
||||||
FileStats []TorrentFileStat `json:"file_stats,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type TorrentFileStat struct {
|
|
||||||
Id int `json:"id,omitempty"`
|
|
||||||
Path string `json:"path,omitempty"`
|
|
||||||
Length int64 `json:"length,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Torrent) Stats() TorrentStats {
|
|
||||||
t.muTorrent.Lock()
|
|
||||||
defer t.muTorrent.Unlock()
|
|
||||||
|
|
||||||
st := TorrentStats{}
|
|
||||||
|
|
||||||
st.Name = t.Name()
|
|
||||||
st.Hash = t.hash.HexString()
|
|
||||||
st.TorrentStatus = t.Status
|
|
||||||
st.TorrentStatusString = t.Status.String()
|
|
||||||
|
|
||||||
if t.Torrent != nil {
|
|
||||||
st.LoadedSize = t.Torrent.BytesCompleted()
|
|
||||||
st.TorrentSize = t.Length()
|
|
||||||
st.PreloadedBytes = t.PreloadedBytes
|
|
||||||
st.PreloadSize = t.PreloadSize
|
|
||||||
st.DownloadSpeed = t.DownloadSpeed
|
|
||||||
st.UploadSpeed = t.UploadSpeed
|
|
||||||
|
|
||||||
tst := t.Torrent.Stats()
|
|
||||||
st.BytesWritten = tst.BytesWritten.Int64()
|
|
||||||
st.BytesWrittenData = tst.BytesWrittenData.Int64()
|
|
||||||
st.BytesRead = tst.BytesRead.Int64()
|
|
||||||
st.BytesReadData = tst.BytesReadData.Int64()
|
|
||||||
st.BytesReadUsefulData = tst.BytesReadUsefulData.Int64()
|
|
||||||
st.ChunksWritten = tst.ChunksWritten.Int64()
|
|
||||||
st.ChunksRead = tst.ChunksRead.Int64()
|
|
||||||
st.ChunksReadUseful = tst.ChunksReadUseful.Int64()
|
|
||||||
st.ChunksReadWasted = tst.ChunksReadWasted.Int64()
|
|
||||||
st.PiecesDirtiedGood = tst.PiecesDirtiedGood.Int64()
|
|
||||||
st.PiecesDirtiedBad = tst.PiecesDirtiedBad.Int64()
|
|
||||||
st.TotalPeers = tst.TotalPeers
|
|
||||||
st.PendingPeers = tst.PendingPeers
|
|
||||||
st.ActivePeers = tst.ActivePeers
|
|
||||||
st.ConnectedSeeders = tst.ConnectedSeeders
|
|
||||||
st.HalfOpenPeers = tst.HalfOpenPeers
|
|
||||||
|
|
||||||
files := t.Files()
|
|
||||||
|
|
||||||
sort.Slice(files, func(i, j int) bool {
|
|
||||||
return files[i].Path() < files[j].Path()
|
|
||||||
})
|
|
||||||
|
|
||||||
for i, f := range files {
|
|
||||||
st.FileStats = append(st.FileStats, TorrentFileStat{
|
|
||||||
Id: i,
|
|
||||||
Path: f.Path(),
|
|
||||||
Length: f.Length(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return st
|
|
||||||
}
|
|
||||||
74
src/server/torr/state/state.go
Normal file
74
src/server/torr/state/state.go
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
package state
|
||||||
|
|
||||||
|
type TorrentStatus int
|
||||||
|
|
||||||
|
func (t TorrentStatus) String() string {
|
||||||
|
switch t {
|
||||||
|
case TorrentAdded:
|
||||||
|
return "Torrent added"
|
||||||
|
case TorrentGettingInfo:
|
||||||
|
return "Torrent getting info"
|
||||||
|
case TorrentPreload:
|
||||||
|
return "Torrent preload"
|
||||||
|
case TorrentWorking:
|
||||||
|
return "Torrent working"
|
||||||
|
case TorrentClosed:
|
||||||
|
return "Torrent closed"
|
||||||
|
case TorrentInDB:
|
||||||
|
return "Torrent in db"
|
||||||
|
default:
|
||||||
|
return "Torrent unknown status"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
TorrentAdded = TorrentStatus(iota)
|
||||||
|
TorrentGettingInfo
|
||||||
|
TorrentPreload
|
||||||
|
TorrentWorking
|
||||||
|
TorrentClosed
|
||||||
|
TorrentInDB
|
||||||
|
)
|
||||||
|
|
||||||
|
type TorrentStats struct {
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
Hash string `json:"hash,omitempty"`
|
||||||
|
|
||||||
|
TorrentStatus TorrentStatus `json:"torrent_status,omitempty"`
|
||||||
|
TorrentStatusString string `json:"torrent_status_string,omitempty"`
|
||||||
|
|
||||||
|
LoadedSize int64 `json:"loaded_size,omitempty"`
|
||||||
|
TorrentSize int64 `json:"torrent_size,omitempty"`
|
||||||
|
|
||||||
|
PreloadedBytes int64 `json:"preloaded_bytes,omitempty"`
|
||||||
|
PreloadSize int64 `json:"preload_size,omitempty"`
|
||||||
|
|
||||||
|
DownloadSpeed float64 `json:"download_speed,omitempty"`
|
||||||
|
UploadSpeed float64 `json:"upload_speed,omitempty"`
|
||||||
|
|
||||||
|
TotalPeers int `json:"total_peers,omitempty"`
|
||||||
|
PendingPeers int `json:"pending_peers,omitempty"`
|
||||||
|
ActivePeers int `json:"active_peers,omitempty"`
|
||||||
|
ConnectedSeeders int `json:"connected_seeders,omitempty"`
|
||||||
|
HalfOpenPeers int `json:"half_open_peers,omitempty"`
|
||||||
|
|
||||||
|
BytesWritten int64 `json:"bytes_written,omitempty"`
|
||||||
|
BytesWrittenData int64 `json:"bytes_written_data,omitempty"`
|
||||||
|
BytesRead int64 `json:"bytes_read,omitempty"`
|
||||||
|
BytesReadData int64 `json:"bytes_read_data,omitempty"`
|
||||||
|
BytesReadUsefulData int64 `json:"bytes_read_useful_data,omitempty"`
|
||||||
|
ChunksWritten int64 `json:"chunks_written,omitempty"`
|
||||||
|
ChunksRead int64 `json:"chunks_read,omitempty"`
|
||||||
|
ChunksReadUseful int64 `json:"chunks_read_useful,omitempty"`
|
||||||
|
ChunksReadWasted int64 `json:"chunks_read_wasted,omitempty"`
|
||||||
|
PiecesDirtiedGood int64 `json:"pieces_dirtied_good,omitempty"`
|
||||||
|
PiecesDirtiedBad int64 `json:"pieces_dirtied_bad,omitempty"`
|
||||||
|
|
||||||
|
FileStats []TorrentFileStat `json:"file_stats,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TorrentFileStat struct {
|
||||||
|
Id int `json:"id,omitempty"`
|
||||||
|
Path string `json:"path,omitempty"`
|
||||||
|
Length int64 `json:"length,omitempty"`
|
||||||
|
}
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
package torrstor
|
package torrstor
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
|
||||||
"sort"
|
"sort"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/anacrolix/torrent"
|
"github.com/anacrolix/torrent"
|
||||||
|
"server/log"
|
||||||
"server/settings"
|
"server/settings"
|
||||||
"server/torr/utils"
|
"server/torr/utils"
|
||||||
|
|
||||||
@@ -52,7 +52,7 @@ func NewCache(capacity int64, storage *Storage) *Cache {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Cache) Init(info *metainfo.Info, hash metainfo.Hash) {
|
func (c *Cache) Init(info *metainfo.Info, hash metainfo.Hash) {
|
||||||
log.Println("Create cache for:", info.Name)
|
log.TLogln("Create cache for:", info.Name)
|
||||||
if c.capacity == 0 {
|
if c.capacity == 0 {
|
||||||
c.capacity = info.PieceLength * 6
|
c.capacity = info.PieceLength * 6
|
||||||
}
|
}
|
||||||
@@ -89,7 +89,7 @@ func (c *Cache) Piece(m metainfo.Piece) storage.PieceImpl {
|
|||||||
|
|
||||||
func (c *Cache) Close() error {
|
func (c *Cache) Close() error {
|
||||||
c.isRemove = false
|
c.isRemove = false
|
||||||
log.Println("Close cache for:", c.hash)
|
log.TLogln("Close cache for:", c.hash)
|
||||||
if _, ok := c.s.caches[c.hash]; ok {
|
if _, ok := c.s.caches[c.hash]; ok {
|
||||||
delete(c.s.caches, c.hash)
|
delete(c.s.caches, c.hash)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,11 +2,13 @@ package torr
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"sort"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"server/log"
|
||||||
"server/settings"
|
"server/settings"
|
||||||
|
"server/torr/state"
|
||||||
"server/torr/utils"
|
"server/torr/utils"
|
||||||
utils2 "server/utils"
|
utils2 "server/utils"
|
||||||
|
|
||||||
@@ -16,43 +18,13 @@ import (
|
|||||||
"github.com/anacrolix/torrent/metainfo"
|
"github.com/anacrolix/torrent/metainfo"
|
||||||
)
|
)
|
||||||
|
|
||||||
type TorrentStatus int
|
|
||||||
|
|
||||||
func (t TorrentStatus) String() string {
|
|
||||||
switch t {
|
|
||||||
case TorrentAdded:
|
|
||||||
return "Torrent added"
|
|
||||||
case TorrentGettingInfo:
|
|
||||||
return "Torrent getting info"
|
|
||||||
case TorrentPreload:
|
|
||||||
return "Torrent preload"
|
|
||||||
case TorrentWorking:
|
|
||||||
return "Torrent working"
|
|
||||||
case TorrentClosed:
|
|
||||||
return "Torrent closed"
|
|
||||||
case TorrentInDB:
|
|
||||||
return "Torrent in db"
|
|
||||||
default:
|
|
||||||
return "Torrent unknown status"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
TorrentAdded = TorrentStatus(iota)
|
|
||||||
TorrentGettingInfo
|
|
||||||
TorrentPreload
|
|
||||||
TorrentWorking
|
|
||||||
TorrentClosed
|
|
||||||
TorrentInDB
|
|
||||||
)
|
|
||||||
|
|
||||||
type Torrent struct {
|
type Torrent struct {
|
||||||
///// info for db
|
///// info for db
|
||||||
Title string
|
Title string
|
||||||
Poster string
|
Poster string
|
||||||
*torrent.TorrentSpec
|
*torrent.TorrentSpec
|
||||||
|
|
||||||
Status TorrentStatus
|
Status state.TorrentStatus
|
||||||
/////
|
/////
|
||||||
|
|
||||||
*torrent.Torrent
|
*torrent.Torrent
|
||||||
@@ -103,7 +75,7 @@ func NewTorrent(spec *torrent.TorrentSpec, bt *BTServer) (*Torrent, error) {
|
|||||||
|
|
||||||
torr := new(Torrent)
|
torr := new(Torrent)
|
||||||
torr.Torrent = goTorrent
|
torr.Torrent = goTorrent
|
||||||
torr.Status = TorrentAdded
|
torr.Status = state.TorrentAdded
|
||||||
torr.lastTimeSpeed = time.Now()
|
torr.lastTimeSpeed = time.Now()
|
||||||
torr.bt = bt
|
torr.bt = bt
|
||||||
torr.closed = goTorrent.Closed()
|
torr.closed = goTorrent.Closed()
|
||||||
@@ -135,12 +107,12 @@ func (t *Torrent) WaitInfo() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (t *Torrent) GotInfo() bool {
|
func (t *Torrent) GotInfo() bool {
|
||||||
if t.Status == TorrentClosed {
|
if t.Status == state.TorrentClosed {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
t.Status = TorrentGettingInfo
|
t.Status = state.TorrentGettingInfo
|
||||||
if t.WaitInfo() {
|
if t.WaitInfo() {
|
||||||
t.Status = TorrentWorking
|
t.Status = state.TorrentWorking
|
||||||
t.expiredTime = time.Now().Add(time.Minute * 5)
|
t.expiredTime = time.Now().Add(time.Minute * 5)
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
@@ -166,6 +138,7 @@ func (t *Torrent) watch() {
|
|||||||
|
|
||||||
func (t *Torrent) progressEvent() {
|
func (t *Torrent) progressEvent() {
|
||||||
if t.expired() {
|
if t.expired() {
|
||||||
|
log.TLogln("Torrent close by timeout", t.Torrent.InfoHash().HexString())
|
||||||
t.drop()
|
t.drop()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -207,7 +180,7 @@ func (t *Torrent) updateRA() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (t *Torrent) expired() bool {
|
func (t *Torrent) expired() bool {
|
||||||
return t.cache.ReadersLen() == 0 && t.expiredTime.Before(time.Now()) && (t.Status == TorrentWorking || t.Status == TorrentClosed)
|
return t.cache.ReadersLen() == 0 && t.expiredTime.Before(time.Now()) && (t.Status == state.TorrentWorking || t.Status == state.TorrentClosed)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Torrent) Files() []*torrent.File {
|
func (t *Torrent) Files() []*torrent.File {
|
||||||
@@ -230,7 +203,7 @@ func (t *Torrent) Length() int64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (t *Torrent) NewReader(file *torrent.File, readahead int64) *Reader {
|
func (t *Torrent) NewReader(file *torrent.File, readahead int64) *Reader {
|
||||||
if t.Status == TorrentClosed {
|
if t.Status == state.TorrentClosed {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
reader := NewReader(t, file, readahead)
|
reader := NewReader(t, file, readahead)
|
||||||
@@ -252,14 +225,14 @@ func (t *Torrent) Preload(index int, size int64) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if t.Status == TorrentGettingInfo {
|
if t.Status == state.TorrentGettingInfo {
|
||||||
t.WaitInfo()
|
t.WaitInfo()
|
||||||
// wait change status
|
// wait change status
|
||||||
time.Sleep(100 * time.Millisecond)
|
time.Sleep(100 * time.Millisecond)
|
||||||
}
|
}
|
||||||
|
|
||||||
t.muTorrent.Lock()
|
t.muTorrent.Lock()
|
||||||
if t.Status != TorrentWorking {
|
if t.Status != state.TorrentWorking {
|
||||||
t.muTorrent.Unlock()
|
t.muTorrent.Unlock()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -271,12 +244,12 @@ func (t *Torrent) Preload(index int, size int64) {
|
|||||||
t.muTorrent.Unlock()
|
t.muTorrent.Unlock()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
t.Status = TorrentPreload
|
t.Status = state.TorrentPreload
|
||||||
t.muTorrent.Unlock()
|
t.muTorrent.Unlock()
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
if t.Status == TorrentPreload {
|
if t.Status == state.TorrentPreload {
|
||||||
t.Status = TorrentWorking
|
t.Status = state.TorrentWorking
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
@@ -321,10 +294,10 @@ func (t *Torrent) Preload(index int, size int64) {
|
|||||||
t.PreloadSize = size
|
t.PreloadSize = size
|
||||||
var lastSize int64 = 0
|
var lastSize int64 = 0
|
||||||
errCount := 0
|
errCount := 0
|
||||||
for t.Status == TorrentPreload {
|
for t.Status == state.TorrentPreload {
|
||||||
t.expiredTime = time.Now().Add(time.Minute * 5)
|
t.expiredTime = time.Now().Add(time.Minute * 5)
|
||||||
t.PreloadedBytes = t.Torrent.BytesCompleted()
|
t.PreloadedBytes = t.Torrent.BytesCompleted()
|
||||||
log.Println("Preload:", 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:", 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 t.PreloadedBytes >= t.PreloadSize {
|
if t.PreloadedBytes >= t.PreloadSize {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -352,7 +325,7 @@ func (t *Torrent) drop() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (t *Torrent) Close() {
|
func (t *Torrent) Close() {
|
||||||
t.Status = TorrentClosed
|
t.Status = state.TorrentClosed
|
||||||
t.bt.mu.Lock()
|
t.bt.mu.Lock()
|
||||||
defer t.bt.mu.Unlock()
|
defer t.bt.mu.Unlock()
|
||||||
|
|
||||||
@@ -362,3 +335,59 @@ func (t *Torrent) Close() {
|
|||||||
|
|
||||||
t.drop()
|
t.drop()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *Torrent) Stats() *state.TorrentStats {
|
||||||
|
t.muTorrent.Lock()
|
||||||
|
defer t.muTorrent.Unlock()
|
||||||
|
|
||||||
|
st := new(state.TorrentStats)
|
||||||
|
|
||||||
|
st.TorrentStatus = t.Status
|
||||||
|
st.TorrentStatusString = t.Status.String()
|
||||||
|
if t.TorrentSpec != nil {
|
||||||
|
st.Hash = t.TorrentSpec.InfoHash.HexString()
|
||||||
|
}
|
||||||
|
if t.Torrent != nil {
|
||||||
|
st.Name = t.Torrent.Name()
|
||||||
|
st.Hash = t.Torrent.InfoHash().HexString()
|
||||||
|
st.LoadedSize = t.Torrent.BytesCompleted()
|
||||||
|
st.TorrentSize = t.Length()
|
||||||
|
st.PreloadedBytes = t.PreloadedBytes
|
||||||
|
st.PreloadSize = t.PreloadSize
|
||||||
|
st.DownloadSpeed = t.DownloadSpeed
|
||||||
|
st.UploadSpeed = t.UploadSpeed
|
||||||
|
|
||||||
|
tst := t.Torrent.Stats()
|
||||||
|
st.BytesWritten = tst.BytesWritten.Int64()
|
||||||
|
st.BytesWrittenData = tst.BytesWrittenData.Int64()
|
||||||
|
st.BytesRead = tst.BytesRead.Int64()
|
||||||
|
st.BytesReadData = tst.BytesReadData.Int64()
|
||||||
|
st.BytesReadUsefulData = tst.BytesReadUsefulData.Int64()
|
||||||
|
st.ChunksWritten = tst.ChunksWritten.Int64()
|
||||||
|
st.ChunksRead = tst.ChunksRead.Int64()
|
||||||
|
st.ChunksReadUseful = tst.ChunksReadUseful.Int64()
|
||||||
|
st.ChunksReadWasted = tst.ChunksReadWasted.Int64()
|
||||||
|
st.PiecesDirtiedGood = tst.PiecesDirtiedGood.Int64()
|
||||||
|
st.PiecesDirtiedBad = tst.PiecesDirtiedBad.Int64()
|
||||||
|
st.TotalPeers = tst.TotalPeers
|
||||||
|
st.PendingPeers = tst.PendingPeers
|
||||||
|
st.ActivePeers = tst.ActivePeers
|
||||||
|
st.ConnectedSeeders = tst.ConnectedSeeders
|
||||||
|
st.HalfOpenPeers = tst.HalfOpenPeers
|
||||||
|
|
||||||
|
files := t.Files()
|
||||||
|
|
||||||
|
sort.Slice(files, func(i, j int) bool {
|
||||||
|
return files[i].Path() < files[j].Path()
|
||||||
|
})
|
||||||
|
|
||||||
|
for i, f := range files {
|
||||||
|
st.FileStats = append(st.FileStats, state.TorrentFileStat{
|
||||||
|
Id: i,
|
||||||
|
Path: f.Path(),
|
||||||
|
Length: f.Length(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return st
|
||||||
|
}
|
||||||
|
|||||||
89
src/server/utils/filetypes.go
Normal file
89
src/server/utils/filetypes.go
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"server/torr/state"
|
||||||
|
)
|
||||||
|
|
||||||
|
var extVideo = map[string]interface{}{
|
||||||
|
".3g2": nil,
|
||||||
|
".3gp": nil,
|
||||||
|
".aaf": nil,
|
||||||
|
".asf": nil,
|
||||||
|
".avchd": nil,
|
||||||
|
".avi": nil,
|
||||||
|
".drc": nil,
|
||||||
|
".flv": nil,
|
||||||
|
".m2ts": nil,
|
||||||
|
".ts": nil,
|
||||||
|
".m2v": nil,
|
||||||
|
".m4p": nil,
|
||||||
|
".m4v": nil,
|
||||||
|
".mkv": nil,
|
||||||
|
".mng": nil,
|
||||||
|
".mov": nil,
|
||||||
|
".mp2": nil,
|
||||||
|
".mp4": nil,
|
||||||
|
".mpe": nil,
|
||||||
|
".mpeg": nil,
|
||||||
|
".mpg": nil,
|
||||||
|
".mpv": nil,
|
||||||
|
".mxf": nil,
|
||||||
|
".nsv": nil,
|
||||||
|
".ogg": nil,
|
||||||
|
".ogv": nil,
|
||||||
|
".qt": nil,
|
||||||
|
".rm": nil,
|
||||||
|
".rmvb": nil,
|
||||||
|
".roq": nil,
|
||||||
|
".svi": nil,
|
||||||
|
".vob": nil,
|
||||||
|
".webm": nil,
|
||||||
|
".wmv": nil,
|
||||||
|
".yuv": nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
var extAudio = map[string]interface{}{
|
||||||
|
".aac": nil,
|
||||||
|
".aiff": nil,
|
||||||
|
".ape": nil,
|
||||||
|
".au": nil,
|
||||||
|
".flac": nil,
|
||||||
|
".gsm": nil,
|
||||||
|
".it": nil,
|
||||||
|
".m3u": nil,
|
||||||
|
".m4a": nil,
|
||||||
|
".mid": nil,
|
||||||
|
".mod": nil,
|
||||||
|
".mp3": nil,
|
||||||
|
".mpa": nil,
|
||||||
|
".pls": nil,
|
||||||
|
".ra": nil,
|
||||||
|
".s3m": nil,
|
||||||
|
".sid": nil,
|
||||||
|
".wav": nil,
|
||||||
|
".wma": nil,
|
||||||
|
".xm": nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetMimeType(filename string) string {
|
||||||
|
ext := filepath.Ext(filename)
|
||||||
|
if _, ok := extVideo[ext]; ok {
|
||||||
|
return "video/*"
|
||||||
|
}
|
||||||
|
if _, ok := extAudio[ext]; ok {
|
||||||
|
return "audio/*"
|
||||||
|
}
|
||||||
|
return "*/*"
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetPlayableFiles(st state.TorrentStats) []state.TorrentFileStat {
|
||||||
|
files := make([]state.TorrentFileStat, 0)
|
||||||
|
for _, f := range st.FileStats {
|
||||||
|
if GetMimeType(f.Path) != "*/*" {
|
||||||
|
files = append(files, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return files
|
||||||
|
}
|
||||||
109
src/server/web/api/m3u.go
Normal file
109
src/server/web/api/m3u.go
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"path/filepath"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
sets "server/settings"
|
||||||
|
"server/torr/state"
|
||||||
|
"server/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
func allPlayList(c *gin.Context) {
|
||||||
|
_, fromlast := c.GetQuery("fromlast")
|
||||||
|
stats := listTorrents()
|
||||||
|
|
||||||
|
host := "http://" + c.Request.Host
|
||||||
|
list := "#EXTM3U\n"
|
||||||
|
|
||||||
|
for _, stat := range stats {
|
||||||
|
list += getM3uList(stat, host, fromlast)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Header("Content-Type", "audio/x-mpegurl")
|
||||||
|
c.Header("Connection", "close")
|
||||||
|
c.Header("Content-Disposition", `attachment; filename="all.m3u"`)
|
||||||
|
http.ServeContent(c.Writer, c.Request, "all.m3u", time.Now(), bytes.NewReader([]byte(list)))
|
||||||
|
|
||||||
|
c.Status(200)
|
||||||
|
}
|
||||||
|
|
||||||
|
func playList(c *gin.Context) {
|
||||||
|
hash := c.Query("torrhash")
|
||||||
|
_, fromlast := c.GetQuery("fromlast")
|
||||||
|
if hash == "" {
|
||||||
|
c.AbortWithError(http.StatusBadRequest, errors.New("hash is empty"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
stats := listTorrents()
|
||||||
|
var stat *state.TorrentStats
|
||||||
|
for _, st := range stats {
|
||||||
|
if st.Hash == hash {
|
||||||
|
stat = st
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if stat == nil {
|
||||||
|
c.AbortWithStatus(http.StatusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO проверить
|
||||||
|
host := "http://" + c.Request.Host
|
||||||
|
list := getM3uList(stat, host, fromlast)
|
||||||
|
list = "#EXTM3U\n" + list
|
||||||
|
|
||||||
|
c.Header("Content-Type", "audio/x-mpegurl")
|
||||||
|
c.Header("Connection", "close")
|
||||||
|
c.Header("Content-Disposition", `attachment; filename="playlist.m3u"`)
|
||||||
|
http.ServeContent(c.Writer, c.Request, "playlist.m3u", time.Now(), bytes.NewReader([]byte(list)))
|
||||||
|
|
||||||
|
c.Status(200)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getM3uList(tor *state.TorrentStats, host string, fromLast bool) string {
|
||||||
|
m3u := ""
|
||||||
|
from := 0
|
||||||
|
if fromLast {
|
||||||
|
pos := searchLastPlayed(tor)
|
||||||
|
if pos != -1 {
|
||||||
|
from = pos
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for i, f := range tor.FileStats {
|
||||||
|
if i >= from {
|
||||||
|
if utils.GetMimeType(f.Path) != "*/*" {
|
||||||
|
fn := filepath.Base(f.Path)
|
||||||
|
if fn == "" {
|
||||||
|
fn = f.Path
|
||||||
|
}
|
||||||
|
m3u += "#EXTINF:0," + fn + "\n"
|
||||||
|
// http://127.0.0.1:8090/stream/fname?link=...&index=0&play
|
||||||
|
m3u += host + "/stream/" + url.QueryEscape(f.Path) + "?link=" + tor.Hash + "&file=" + fmt.Sprint(f.Id) + "\n"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return m3u
|
||||||
|
}
|
||||||
|
|
||||||
|
func searchLastPlayed(tor *state.TorrentStats) int {
|
||||||
|
//TODO проверить
|
||||||
|
viewed := sets.ListViewed(tor.Hash)
|
||||||
|
for i := len(tor.FileStats); i > 0; i-- {
|
||||||
|
stat := tor.FileStats[i]
|
||||||
|
for _, v := range viewed {
|
||||||
|
if stat.Id == v.FileIndex {
|
||||||
|
return v.FileIndex
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
@@ -25,6 +25,11 @@ func SetupRouteApi(route *gin.Engine, serv *torr.BTServer) {
|
|||||||
|
|
||||||
route.GET("/stream", stream)
|
route.GET("/stream", stream)
|
||||||
route.GET("/stream/*fname", stream)
|
route.GET("/stream/*fname", stream)
|
||||||
|
|
||||||
|
route.POST("/viewed", viewed)
|
||||||
|
|
||||||
|
route.GET("/playlist/all.m3u", allPlayList)
|
||||||
|
route.GET("/playlist", playList)
|
||||||
}
|
}
|
||||||
|
|
||||||
func echo(c *gin.Context) {
|
func echo(c *gin.Context) {
|
||||||
|
|||||||
@@ -13,10 +13,6 @@ type setsReqJS struct {
|
|||||||
requestI
|
requestI
|
||||||
Sets *settings2.BTSets `json:"sets,omitempty"`
|
Sets *settings2.BTSets `json:"sets,omitempty"`
|
||||||
}
|
}
|
||||||
type setsRespJS struct {
|
|
||||||
responseI
|
|
||||||
Sets *settings2.BTSets `json:"sets,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func settings(c *gin.Context) {
|
func settings(c *gin.Context) {
|
||||||
var req setsReqJS
|
var req setsReqJS
|
||||||
@@ -27,9 +23,7 @@ func settings(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if req.Action == "get" {
|
if req.Action == "get" {
|
||||||
resp := setsRespJS{}
|
c.JSON(200, settings2.BTsets)
|
||||||
resp.Sets = settings2.BTsets
|
|
||||||
c.JSON(200, resp)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if req.Action == "set" {
|
if req.Action == "set" {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package api
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
@@ -10,6 +11,13 @@ import (
|
|||||||
"server/web/api/utils"
|
"server/web/api/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// http://127.0.0.1:8090/stream/fname?link=...&index=0&stat
|
||||||
|
// http://127.0.0.1:8090/stream/fname?link=...&index=0&m3u
|
||||||
|
// http://127.0.0.1:8090/stream/fname?link=...&index=0&play
|
||||||
|
// http://127.0.0.1:8090/stream/fname?link=...&save&title=...&poster=...
|
||||||
|
// http://127.0.0.1:8090/stream/fname?link=...&index=0&play&save
|
||||||
|
// http://127.0.0.1:8090/stream/fname?link=...&index=0&play&save&title=...&poster=...
|
||||||
|
|
||||||
func stream(c *gin.Context) {
|
func stream(c *gin.Context) {
|
||||||
link := c.Query("link")
|
link := c.Query("link")
|
||||||
indexStr := c.Query("index")
|
indexStr := c.Query("index")
|
||||||
@@ -21,13 +29,19 @@ func stream(c *gin.Context) {
|
|||||||
title := c.Query("title")
|
title := c.Query("title")
|
||||||
poster := c.Query("poster")
|
poster := c.Query("poster")
|
||||||
|
|
||||||
// TODO unescape args
|
|
||||||
|
|
||||||
if link == "" {
|
if link == "" {
|
||||||
c.AbortWithError(http.StatusBadRequest, errors.New("link should not be empty"))
|
c.AbortWithError(http.StatusBadRequest, errors.New("link should not be empty"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if title == "" {
|
||||||
|
title = c.Param("fname")
|
||||||
|
}
|
||||||
|
|
||||||
|
link, _ = url.QueryUnescape(link)
|
||||||
|
title, _ = url.QueryUnescape(title)
|
||||||
|
poster, _ = url.QueryUnescape(poster)
|
||||||
|
|
||||||
spec, err := utils.ParseLink(link)
|
spec, err := utils.ParseLink(link)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.AbortWithError(http.StatusInternalServerError, err)
|
c.AbortWithError(http.StatusInternalServerError, err)
|
||||||
@@ -65,6 +79,7 @@ func stream(c *gin.Context) {
|
|||||||
// save to db
|
// save to db
|
||||||
if save {
|
if save {
|
||||||
utils.AddTorrent(tor)
|
utils.AddTorrent(tor)
|
||||||
|
c.Status(200)
|
||||||
}
|
}
|
||||||
// wait torrent info
|
// wait torrent info
|
||||||
if !tor.WaitInfo() {
|
if !tor.WaitInfo() {
|
||||||
@@ -94,13 +109,13 @@ func stream(c *gin.Context) {
|
|||||||
if stat || (!m3u && !play) {
|
if stat || (!m3u && !play) {
|
||||||
c.JSON(200, tor.Stats())
|
c.JSON(200, tor.Stats())
|
||||||
return
|
return
|
||||||
}
|
} else
|
||||||
// return m3u if query
|
// return m3u if query
|
||||||
if m3u {
|
if m3u {
|
||||||
//TODO m3u
|
//TODO m3u
|
||||||
c.JSON(200, tor.Stats())
|
c.JSON(200, tor.Stats())
|
||||||
return
|
return
|
||||||
}
|
} else
|
||||||
// return play if query
|
// return play if query
|
||||||
if play {
|
if play {
|
||||||
tor.Stream(index, c.Request, c.Writer)
|
tor.Stream(index, c.Request, c.Writer)
|
||||||
|
|||||||
@@ -5,11 +5,14 @@ import (
|
|||||||
|
|
||||||
"github.com/anacrolix/torrent/metainfo"
|
"github.com/anacrolix/torrent/metainfo"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"server/log"
|
||||||
"server/torr"
|
"server/torr"
|
||||||
|
"server/torr/state"
|
||||||
"server/web/api/utils"
|
"server/web/api/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
//Action: add, get, rem, list
|
//Action: add, get, rem, list, drop
|
||||||
type torrReqJS struct {
|
type torrReqJS struct {
|
||||||
requestI
|
requestI
|
||||||
Link string `json:"link,omitempty"`
|
Link string `json:"link,omitempty"`
|
||||||
@@ -26,43 +29,62 @@ func torrents(c *gin.Context) {
|
|||||||
c.AbortWithError(http.StatusBadRequest, err)
|
c.AbortWithError(http.StatusBadRequest, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
c.Status(http.StatusBadRequest)
|
||||||
switch req.Action {
|
switch req.Action {
|
||||||
case "add":
|
case "add":
|
||||||
{
|
{
|
||||||
add(req, c)
|
addTorrent(req, c)
|
||||||
}
|
}
|
||||||
case "get":
|
case "get":
|
||||||
{
|
{
|
||||||
get(req, c)
|
getTorrent(req, c)
|
||||||
}
|
}
|
||||||
case "rem":
|
case "rem":
|
||||||
{
|
{
|
||||||
rem(req, c)
|
remTorrent(req, c)
|
||||||
}
|
}
|
||||||
case "list":
|
case "list":
|
||||||
{
|
{
|
||||||
list(req, c)
|
listTorrent(req, c)
|
||||||
|
}
|
||||||
|
case "drop":
|
||||||
|
{
|
||||||
|
dropTorrent(req, c)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func add(req torrReqJS, c *gin.Context) {
|
func addTorrent(req torrReqJS, c *gin.Context) {
|
||||||
|
log.TLogln("add torrent", req.Link)
|
||||||
torrSpec, err := utils.ParseLink(req.Link)
|
torrSpec, err := utils.ParseLink(req.Link)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log.TLogln("error add torrent:", err)
|
||||||
c.AbortWithError(http.StatusBadRequest, err)
|
c.AbortWithError(http.StatusBadRequest, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
torr, err := torr.NewTorrent(torrSpec, bts)
|
torr, err := torr.NewTorrent(torrSpec, bts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log.TLogln("error add torrent:", err)
|
||||||
c.AbortWithError(http.StatusInternalServerError, err)
|
c.AbortWithError(http.StatusInternalServerError, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !torr.WaitInfo() {
|
||||||
|
log.TLogln("error add torrent:", "timeout connection torrent")
|
||||||
|
c.AbortWithError(http.StatusNotFound, errors.New("timeout connection torrent"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
torr.Title = req.Title
|
torr.Title = req.Title
|
||||||
torr.Poster = req.Poster
|
torr.Poster = req.Poster
|
||||||
|
|
||||||
|
if torr.Title == "" {
|
||||||
|
torr.Title = torr.Name()
|
||||||
|
}
|
||||||
|
|
||||||
if req.SaveToDB {
|
if req.SaveToDB {
|
||||||
|
log.TLogln("save to db:", torr.Torrent.InfoHash().HexString())
|
||||||
utils.AddTorrent(torr)
|
utils.AddTorrent(torr)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,7 +92,7 @@ func add(req torrReqJS, c *gin.Context) {
|
|||||||
c.JSON(200, st)
|
c.JSON(200, st)
|
||||||
}
|
}
|
||||||
|
|
||||||
func get(req torrReqJS, c *gin.Context) {
|
func getTorrent(req torrReqJS, c *gin.Context) {
|
||||||
hash := metainfo.NewHashFromHex(req.Hash)
|
hash := metainfo.NewHashFromHex(req.Hash)
|
||||||
tor := bts.GetTorrent(hash)
|
tor := bts.GetTorrent(hash)
|
||||||
if tor == nil {
|
if tor == nil {
|
||||||
@@ -85,17 +107,22 @@ func get(req torrReqJS, c *gin.Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func rem(req torrReqJS, c *gin.Context) {
|
func remTorrent(req torrReqJS, c *gin.Context) {
|
||||||
hash := metainfo.NewHashFromHex(req.Hash)
|
hash := metainfo.NewHashFromHex(req.Hash)
|
||||||
bts.RemoveTorrent(hash)
|
bts.RemoveTorrent(hash)
|
||||||
utils.RemTorrent(hash)
|
utils.RemTorrent(hash)
|
||||||
c.Status(200)
|
c.Status(200)
|
||||||
}
|
}
|
||||||
|
|
||||||
func list(req torrReqJS, c *gin.Context) {
|
func listTorrent(req torrReqJS, c *gin.Context) {
|
||||||
|
stats := listTorrents()
|
||||||
|
c.JSON(200, stats)
|
||||||
|
}
|
||||||
|
|
||||||
|
func listTorrents() []*state.TorrentStats {
|
||||||
btlist := bts.ListTorrents()
|
btlist := bts.ListTorrents()
|
||||||
dblist := utils.ListTorrents()
|
dblist := utils.ListTorrents()
|
||||||
var stats []torr.TorrentStats
|
var stats []*state.TorrentStats
|
||||||
for _, tr := range btlist {
|
for _, tr := range btlist {
|
||||||
stats = append(stats, tr.Stats())
|
stats = append(stats, tr.Stats())
|
||||||
}
|
}
|
||||||
@@ -109,6 +136,16 @@ mainloop:
|
|||||||
}
|
}
|
||||||
stats = append(stats, db.Stats())
|
stats = append(stats, db.Stats())
|
||||||
}
|
}
|
||||||
|
return stats
|
||||||
c.JSON(200, stats)
|
}
|
||||||
|
|
||||||
|
func dropTorrent(req torrReqJS, c *gin.Context) {
|
||||||
|
if req.Hash == "" {
|
||||||
|
c.AbortWithError(http.StatusBadRequest, errors.New("hash is empty"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
hash := metainfo.NewHashFromHex(req.Hash)
|
||||||
|
|
||||||
|
bts.RemoveTorrent(hash)
|
||||||
|
c.Status(200)
|
||||||
}
|
}
|
||||||
|
|||||||
53
src/server/web/api/viewed.go
Normal file
53
src/server/web/api/viewed.go
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
sets "server/settings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Action: set, rem, list
|
||||||
|
type viewedReqJS struct {
|
||||||
|
requestI
|
||||||
|
*sets.Viewed
|
||||||
|
}
|
||||||
|
|
||||||
|
func viewed(c *gin.Context) {
|
||||||
|
var req viewedReqJS
|
||||||
|
err := c.ShouldBindJSON(&req)
|
||||||
|
if err != nil {
|
||||||
|
c.AbortWithError(http.StatusBadRequest, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch req.Action {
|
||||||
|
case "set":
|
||||||
|
{
|
||||||
|
setViewed(req, c)
|
||||||
|
}
|
||||||
|
case "rem":
|
||||||
|
{
|
||||||
|
remViewed(req, c)
|
||||||
|
}
|
||||||
|
case "list":
|
||||||
|
{
|
||||||
|
listViewed(req, c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func setViewed(req viewedReqJS, c *gin.Context) {
|
||||||
|
sets.SetViewed(req.Viewed)
|
||||||
|
c.Status(200)
|
||||||
|
}
|
||||||
|
|
||||||
|
func remViewed(req viewedReqJS, c *gin.Context) {
|
||||||
|
sets.RemViewed(req.Viewed)
|
||||||
|
c.Status(200)
|
||||||
|
}
|
||||||
|
|
||||||
|
func listViewed(req viewedReqJS, c *gin.Context) {
|
||||||
|
list := sets.ListViewed(req.Hash)
|
||||||
|
c.JSON(200, list)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user