mirror of
https://github.com/Ernous/TorrServerJellyfin.git
synced 2025-12-19 21:46:11 +05:00
creating...
This commit is contained in:
82
src/server/settings/torrent.go
Normal file
82
src/server/settings/torrent.go
Normal file
@@ -0,0 +1,82 @@
|
||||
package settings
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"sort"
|
||||
"sync"
|
||||
|
||||
"github.com/anacrolix/torrent"
|
||||
"github.com/anacrolix/torrent/metainfo"
|
||||
"server/torr"
|
||||
)
|
||||
|
||||
type TorrentDB struct {
|
||||
*torrent.TorrentSpec
|
||||
|
||||
Title string `json:"title,omitempty"`
|
||||
Poster string `json:"poster,omitempty"`
|
||||
|
||||
Timestamp int64 `json:"timestamp,omitempty"`
|
||||
|
||||
Files []torr.TorrentFileStat `json:"files,omitempty"`
|
||||
}
|
||||
|
||||
type File struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
Id int `json:"id,omitempty"`
|
||||
Size int64 `json:"size,omitempty"`
|
||||
}
|
||||
|
||||
var mu sync.Mutex
|
||||
|
||||
func AddTorrent(torr *TorrentDB) {
|
||||
list := ListTorrent()
|
||||
mu.Lock()
|
||||
find := -1
|
||||
for i, db := range list {
|
||||
if db.InfoHash.HexString() == torr.InfoHash.HexString() {
|
||||
find = i
|
||||
break
|
||||
}
|
||||
}
|
||||
if find != -1 {
|
||||
list[find] = torr
|
||||
} else {
|
||||
list = append(list, torr)
|
||||
}
|
||||
for _, db := range list {
|
||||
buf, err := json.Marshal(db)
|
||||
if err == nil {
|
||||
tdb.Set("Torrents", db.InfoHash.HexString(), buf)
|
||||
}
|
||||
}
|
||||
mu.Unlock()
|
||||
}
|
||||
|
||||
func ListTorrent() []*TorrentDB {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
|
||||
var list []*TorrentDB
|
||||
keys := tdb.List("Torrents")
|
||||
for _, key := range keys {
|
||||
buf := tdb.Get("Torrents", key)
|
||||
if len(buf) > 0 {
|
||||
var torr *TorrentDB
|
||||
err := json.Unmarshal(buf, torr)
|
||||
if err == nil {
|
||||
list = append(list, torr)
|
||||
}
|
||||
}
|
||||
}
|
||||
sort.Slice(list, func(i, j int) bool {
|
||||
return list[i].Timestamp > list[j].Timestamp
|
||||
})
|
||||
return list
|
||||
}
|
||||
|
||||
func RemTorrent(hash metainfo.Hash) {
|
||||
mu.Lock()
|
||||
tdb.Rem("Torrents", hash.HexString())
|
||||
mu.Unlock()
|
||||
}
|
||||
@@ -99,6 +99,21 @@ func (bt *BTServer) configure() {
|
||||
log.Println("Configure client:", settings.BTsets)
|
||||
}
|
||||
|
||||
func (bt *BTServer) GetTorrent(hash torrent.InfoHash) *Torrent {
|
||||
if torr, ok := bt.torrents[hash]; ok {
|
||||
return torr
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (bt *BTServer) ListTorrents() []*Torrent {
|
||||
var list []*Torrent
|
||||
for _, t := range bt.torrents {
|
||||
list = append(list, t)
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
func (bt *BTServer) RemoveTorrent(hash torrent.InfoHash) {
|
||||
if torr, ok := bt.torrents[hash]; ok {
|
||||
torr.Close()
|
||||
|
||||
@@ -33,46 +33,46 @@ func (bt *BTServer) BTState() *BTState {
|
||||
}
|
||||
|
||||
type TorrentStats struct {
|
||||
Name string
|
||||
Hash string
|
||||
Name string `json:"name,omitempty"`
|
||||
Hash string `json:"hash,omitempty"`
|
||||
|
||||
TorrentStatus TorrentStatus
|
||||
TorrentStatusString string
|
||||
TorrentStatus TorrentStatus `json:"torrent_status,omitempty"`
|
||||
TorrentStatusString string `json:"torrent_status_string,omitempty"`
|
||||
|
||||
LoadedSize int64
|
||||
TorrentSize int64
|
||||
LoadedSize int64 `json:"loaded_size,omitempty"`
|
||||
TorrentSize int64 `json:"torrent_size,omitempty"`
|
||||
|
||||
PreloadedBytes int64
|
||||
PreloadSize int64
|
||||
PreloadedBytes int64 `json:"preloaded_bytes,omitempty"`
|
||||
PreloadSize int64 `json:"preload_size,omitempty"`
|
||||
|
||||
DownloadSpeed float64
|
||||
UploadSpeed float64
|
||||
DownloadSpeed float64 `json:"download_speed,omitempty"`
|
||||
UploadSpeed float64 `json:"upload_speed,omitempty"`
|
||||
|
||||
TotalPeers int
|
||||
PendingPeers int
|
||||
ActivePeers int
|
||||
ConnectedSeeders int
|
||||
HalfOpenPeers int
|
||||
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
|
||||
BytesWrittenData int64
|
||||
BytesRead int64
|
||||
BytesReadData int64
|
||||
BytesReadUsefulData int64
|
||||
ChunksWritten int64
|
||||
ChunksRead int64
|
||||
ChunksReadUseful int64
|
||||
ChunksReadWasted int64
|
||||
PiecesDirtiedGood int64
|
||||
PiecesDirtiedBad int64
|
||||
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
|
||||
FileStats []TorrentFileStat `json:"file_stats,omitempty"`
|
||||
}
|
||||
|
||||
type TorrentFileStat struct {
|
||||
Id int
|
||||
Path string
|
||||
Length int64
|
||||
Id int `json:"id,omitempty"`
|
||||
Path string `json:"path,omitempty"`
|
||||
Length int64 `json:"length,omitempty"`
|
||||
}
|
||||
|
||||
func (t *Torrent) Stats() TorrentStats {
|
||||
@@ -83,8 +83,8 @@ func (t *Torrent) Stats() TorrentStats {
|
||||
|
||||
st.Name = t.Name()
|
||||
st.Hash = t.hash.HexString()
|
||||
st.TorrentStatus = t.status
|
||||
st.TorrentStatusString = t.status.String()
|
||||
st.TorrentStatus = t.Status
|
||||
st.TorrentStatusString = t.Status.String()
|
||||
|
||||
if t.Torrent != nil {
|
||||
st.LoadedSize = t.Torrent.BytesCompleted()
|
||||
|
||||
@@ -30,6 +30,8 @@ func (t TorrentStatus) String() string {
|
||||
return "Torrent working"
|
||||
case TorrentClosed:
|
||||
return "Torrent closed"
|
||||
case TorrentInDB:
|
||||
return "Torrent in db"
|
||||
default:
|
||||
return "Torrent unknown status"
|
||||
}
|
||||
@@ -41,13 +43,19 @@ const (
|
||||
TorrentPreload
|
||||
TorrentWorking
|
||||
TorrentClosed
|
||||
TorrentInDB
|
||||
)
|
||||
|
||||
type Torrent struct {
|
||||
///// info for db
|
||||
Title string
|
||||
Poster string
|
||||
*torrent.TorrentSpec
|
||||
|
||||
Status TorrentStatus
|
||||
/////
|
||||
|
||||
*torrent.Torrent
|
||||
|
||||
status TorrentStatus
|
||||
|
||||
muTorrent sync.Mutex
|
||||
|
||||
bt *BTServer
|
||||
@@ -95,11 +103,11 @@ func NewTorrent(spec *torrent.TorrentSpec, bt *BTServer) (*Torrent, error) {
|
||||
|
||||
torr := new(Torrent)
|
||||
torr.Torrent = goTorrent
|
||||
torr.status = TorrentAdded
|
||||
torr.Status = TorrentAdded
|
||||
torr.lastTimeSpeed = time.Now()
|
||||
torr.bt = bt
|
||||
torr.hash = spec.InfoHash
|
||||
torr.closed = goTorrent.Closed()
|
||||
torr.TorrentSpec = spec
|
||||
|
||||
go torr.watch()
|
||||
|
||||
@@ -127,12 +135,12 @@ func (t *Torrent) WaitInfo() bool {
|
||||
}
|
||||
|
||||
func (t *Torrent) GotInfo() bool {
|
||||
if t.status == TorrentClosed {
|
||||
if t.Status == TorrentClosed {
|
||||
return false
|
||||
}
|
||||
t.status = TorrentGettingInfo
|
||||
t.Status = TorrentGettingInfo
|
||||
if t.WaitInfo() {
|
||||
t.status = TorrentWorking
|
||||
t.Status = TorrentWorking
|
||||
t.expiredTime = time.Now().Add(time.Minute * 5)
|
||||
return true
|
||||
} else {
|
||||
@@ -199,7 +207,7 @@ func (t *Torrent) updateRA() {
|
||||
}
|
||||
|
||||
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 == TorrentWorking || t.Status == TorrentClosed)
|
||||
}
|
||||
|
||||
func (t *Torrent) Files() []*torrent.File {
|
||||
@@ -214,10 +222,6 @@ func (t *Torrent) Hash() metainfo.Hash {
|
||||
return t.hash
|
||||
}
|
||||
|
||||
func (t *Torrent) Status() TorrentStatus {
|
||||
return t.status
|
||||
}
|
||||
|
||||
func (t *Torrent) Length() int64 {
|
||||
if t.Info() == nil {
|
||||
return 0
|
||||
@@ -226,7 +230,7 @@ func (t *Torrent) Length() int64 {
|
||||
}
|
||||
|
||||
func (t *Torrent) NewReader(file *torrent.File, readahead int64) *Reader {
|
||||
if t.status == TorrentClosed {
|
||||
if t.Status == TorrentClosed {
|
||||
return nil
|
||||
}
|
||||
reader := NewReader(t, file, readahead)
|
||||
@@ -243,19 +247,19 @@ func (t *Torrent) GetCache() *torrstor.Cache {
|
||||
return t.cache
|
||||
}
|
||||
|
||||
func (t *Torrent) Preload(file *torrent.File, size int64) {
|
||||
func (t *Torrent) Preload(index int, size int64) {
|
||||
if size < 0 {
|
||||
return
|
||||
}
|
||||
|
||||
if t.status == TorrentGettingInfo {
|
||||
if t.Status == TorrentGettingInfo {
|
||||
t.WaitInfo()
|
||||
// wait change status
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
}
|
||||
|
||||
t.muTorrent.Lock()
|
||||
if t.status != TorrentWorking {
|
||||
if t.Status != TorrentWorking {
|
||||
t.muTorrent.Unlock()
|
||||
return
|
||||
}
|
||||
@@ -267,15 +271,20 @@ func (t *Torrent) Preload(file *torrent.File, size int64) {
|
||||
t.muTorrent.Unlock()
|
||||
return
|
||||
}
|
||||
t.status = TorrentPreload
|
||||
t.Status = TorrentPreload
|
||||
t.muTorrent.Unlock()
|
||||
|
||||
defer func() {
|
||||
if t.status == TorrentPreload {
|
||||
t.status = TorrentWorking
|
||||
if t.Status == TorrentPreload {
|
||||
t.Status = TorrentWorking
|
||||
}
|
||||
}()
|
||||
|
||||
if index < 0 || index >= len(t.Files()) {
|
||||
index = 0
|
||||
}
|
||||
file := t.Files()[index]
|
||||
|
||||
buff5mb := int64(5 * 1024 * 1024)
|
||||
startPreloadLength := size
|
||||
endPreloadOffset := int64(0)
|
||||
@@ -312,7 +321,7 @@ func (t *Torrent) Preload(file *torrent.File, size int64) {
|
||||
t.PreloadSize = size
|
||||
var lastSize int64 = 0
|
||||
errCount := 0
|
||||
for t.status == TorrentPreload {
|
||||
for t.Status == TorrentPreload {
|
||||
t.expiredTime = time.Now().Add(time.Minute * 5)
|
||||
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)
|
||||
@@ -343,7 +352,7 @@ func (t *Torrent) drop() {
|
||||
}
|
||||
|
||||
func (t *Torrent) Close() {
|
||||
t.status = TorrentClosed
|
||||
t.Status = TorrentClosed
|
||||
t.bt.mu.Lock()
|
||||
defer t.bt.mu.Unlock()
|
||||
|
||||
|
||||
@@ -3,6 +3,13 @@ package api
|
||||
/*
|
||||
API
|
||||
|
||||
respnose JSON{
|
||||
|
||||
}
|
||||
request JSON{
|
||||
Action string
|
||||
}
|
||||
|
||||
echo version
|
||||
|
||||
{
|
||||
|
||||
32
src/server/web/api/route.go
Normal file
32
src/server/web/api/route.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"server/torr"
|
||||
"server/version"
|
||||
)
|
||||
|
||||
type requestI struct {
|
||||
Action string `json:"action,omitempty"`
|
||||
}
|
||||
|
||||
type responseI struct {
|
||||
}
|
||||
|
||||
var bts *torr.BTServer
|
||||
|
||||
func SetupRouteApi(route *gin.Engine, serv *torr.BTServer) {
|
||||
bts = serv
|
||||
route.GET("/echo", echo)
|
||||
|
||||
route.POST("/settings", settings)
|
||||
|
||||
route.POST("/torrents", torrents)
|
||||
|
||||
route.GET("/stream", stream)
|
||||
route.GET("/stream/*fname", stream)
|
||||
}
|
||||
|
||||
func echo(c *gin.Context) {
|
||||
c.String(200, "{\"version\": \"%v\"}", version.Version)
|
||||
}
|
||||
41
src/server/web/api/settings.go
Normal file
41
src/server/web/api/settings.go
Normal file
@@ -0,0 +1,41 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/pkg/errors"
|
||||
settings2 "server/settings"
|
||||
)
|
||||
|
||||
//Action: get, set
|
||||
type setsReqJS struct {
|
||||
requestI
|
||||
Sets *settings2.BTSets `json:"sets,omitempty"`
|
||||
}
|
||||
type setsRespJS struct {
|
||||
responseI
|
||||
Sets *settings2.BTSets `json:"sets,omitempty"`
|
||||
}
|
||||
|
||||
func settings(c *gin.Context) {
|
||||
var req setsReqJS
|
||||
err := c.ShouldBindJSON(&req)
|
||||
if err != nil {
|
||||
c.AbortWithError(http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
|
||||
if req.Action == "get" {
|
||||
resp := setsRespJS{}
|
||||
resp.Sets = settings2.BTsets
|
||||
c.JSON(200, resp)
|
||||
return
|
||||
}
|
||||
if req.Action == "set" {
|
||||
settings2.SetBTSets(req.Sets)
|
||||
c.Status(200)
|
||||
return
|
||||
}
|
||||
c.AbortWithError(http.StatusBadRequest, errors.New("action is empty"))
|
||||
}
|
||||
197
src/server/web/api/stream.go
Normal file
197
src/server/web/api/stream.go
Normal file
@@ -0,0 +1,197 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/anacrolix/missinggo/httptoo"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/pkg/errors"
|
||||
"server/torr"
|
||||
"server/web/api/utils"
|
||||
)
|
||||
|
||||
func stream(c *gin.Context) {
|
||||
link := c.Query("link")
|
||||
indexStr := c.Query("index")
|
||||
_, preload := c.GetQuery("preload")
|
||||
_, stat := c.GetQuery("stat")
|
||||
_, save := c.GetQuery("save")
|
||||
_, m3u := c.GetQuery("m3u")
|
||||
_, play := c.GetQuery("play")
|
||||
|
||||
if link == "" {
|
||||
c.AbortWithError(http.StatusBadRequest, errors.New("link should not be empty"))
|
||||
return
|
||||
}
|
||||
|
||||
spec, err := utils.ParseLink(link)
|
||||
if err != nil {
|
||||
c.AbortWithError(http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
|
||||
var tor *torr.Torrent
|
||||
|
||||
// find torrent in bts
|
||||
for _, torrent := range bts.ListTorrents() {
|
||||
if torrent.Hash().HexString() == spec.InfoHash.HexString() {
|
||||
tor = torrent
|
||||
}
|
||||
}
|
||||
// find in db
|
||||
for _, torrent := range utils.ListTorrents() {
|
||||
if torrent.Hash().HexString() == spec.InfoHash.HexString() {
|
||||
tor = torrent
|
||||
}
|
||||
}
|
||||
// add torrent to bts
|
||||
if tor != nil {
|
||||
tor, err = torr.NewTorrent(tor.TorrentSpec, bts)
|
||||
} else {
|
||||
tor, err = torr.NewTorrent(spec, bts)
|
||||
if err != nil {
|
||||
c.AbortWithError(http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
// save to db
|
||||
if save {
|
||||
utils.AddTorrent(tor)
|
||||
}
|
||||
// wait torrent info
|
||||
if !tor.WaitInfo() {
|
||||
c.AbortWithError(http.StatusInternalServerError, errors.New("timeout torrent get info"))
|
||||
return
|
||||
}
|
||||
|
||||
// find file
|
||||
index := -1
|
||||
if len(tor.Files()) == 1 {
|
||||
index = 0
|
||||
} else {
|
||||
ind, err := strconv.Atoi(indexStr)
|
||||
if err == nil {
|
||||
index = ind
|
||||
}
|
||||
}
|
||||
if index == -1 {
|
||||
c.AbortWithError(http.StatusBadRequest, errors.New("\"index\" is empty or wrong"))
|
||||
return
|
||||
}
|
||||
// preload torrent
|
||||
if preload {
|
||||
tor.Preload(index, 0)
|
||||
}
|
||||
// return stat if query
|
||||
if stat || (!m3u && !play) {
|
||||
c.JSON(200, tor.Stats())
|
||||
return
|
||||
}
|
||||
// return m3u if query
|
||||
if m3u {
|
||||
//TODO m3u
|
||||
c.JSON(200, tor.Stats())
|
||||
return
|
||||
}
|
||||
// return play if query
|
||||
if play {
|
||||
tor.Stream(index, c.Request, c.Writer)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
func torrentPlay(c echo.Context) error {
|
||||
link := c.QueryParam("link")
|
||||
if link == "" {
|
||||
return echo.NewHTTPError(http.StatusBadRequest, "link should not be empty")
|
||||
}
|
||||
if settings.Get().EnableDebug {
|
||||
fmt.Println("Play:", c.QueryParams()) // mute log flood on play
|
||||
}
|
||||
qsave := c.QueryParam("save")
|
||||
qpreload := c.QueryParam("preload")
|
||||
qfile := c.QueryParam("file")
|
||||
qstat := c.QueryParam("stat")
|
||||
mm3u := c.QueryParam("m3u")
|
||||
|
||||
preload := int64(0)
|
||||
stat := strings.ToLower(qstat) == "true"
|
||||
|
||||
if qpreload != "" {
|
||||
preload, _ = strconv.ParseInt(qpreload, 10, 64)
|
||||
if preload > 0 {
|
||||
preload *= 1024 * 1024
|
||||
}
|
||||
}
|
||||
|
||||
magnet, infoBytes, err := helpers.GetMagnet(link)
|
||||
if err != nil {
|
||||
fmt.Println("Error get magnet:", link, err)
|
||||
return echo.NewHTTPError(http.StatusBadRequest, err.Error())
|
||||
}
|
||||
|
||||
tor := bts.GetTorrent(magnet.InfoHash)
|
||||
if tor == nil {
|
||||
tor, err = bts.AddTorrent(*magnet, infoBytes, nil)
|
||||
if err != nil {
|
||||
return echo.NewHTTPError(http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
if stat {
|
||||
return c.JSON(http.StatusOK, getTorPlayState(tor))
|
||||
}
|
||||
|
||||
if !tor.WaitInfo() {
|
||||
return echo.NewHTTPError(http.StatusBadRequest, "torrent closed befor get info")
|
||||
}
|
||||
|
||||
if strings.ToLower(qsave) == "true" {
|
||||
if t, err := settings.LoadTorrentDB(magnet.InfoHash.HexString()); t == nil && err == nil {
|
||||
torrDb := toTorrentDB(tor)
|
||||
if torrDb != nil {
|
||||
torrDb.InfoBytes = infoBytes
|
||||
settings.SaveTorrentDB(torrDb)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if strings.ToLower(mm3u) == "true" {
|
||||
mt := tor.Torrent.Metainfo()
|
||||
m3u := helpers.MakeM3UPlayList(tor.Stats(), mt.Magnet(tor.Name(), tor.Hash()).String(), c.Scheme()+"://"+c.Request().Host)
|
||||
c.Response().Header().Set("Content-Type", "audio/x-mpegurl")
|
||||
c.Response().Header().Set("Connection", "close")
|
||||
name := utils.CleanFName(tor.Name()) + ".m3u"
|
||||
c.Response().Header().Set("ETag", httptoo.EncodeQuotedString(fmt.Sprintf("%s/%s", tor.Hash().HexString(), name)))
|
||||
c.Response().Header().Set("Content-Disposition", `attachment; filename="`+name+`"`)
|
||||
http.ServeContent(c.Response(), c.Request(), name, time.Now(), bytes.NewReader([]byte(m3u)))
|
||||
return c.NoContent(http.StatusOK)
|
||||
}
|
||||
|
||||
files := helpers.GetPlayableFiles(tor.Stats())
|
||||
|
||||
if len(files) == 1 {
|
||||
file := helpers.FindFile(files[0].Id, tor)
|
||||
if file == nil {
|
||||
return echo.NewHTTPError(http.StatusInternalServerError, fmt.Sprint("File", files[0], "not found in torrent", tor.Name()))
|
||||
}
|
||||
|
||||
return bts.Play(tor, file, preload, c)
|
||||
}
|
||||
|
||||
if qfile == "" && len(files) > 1 {
|
||||
return c.JSON(http.StatusOK, getTorPlayState(tor))
|
||||
}
|
||||
|
||||
fileInd, _ := strconv.Atoi(qfile)
|
||||
file := helpers.FindFile(fileInd, tor)
|
||||
if file == nil {
|
||||
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprint("File index ", fileInd, " not found in torrent ", tor.Name()))
|
||||
}
|
||||
return bts.Play(tor, file, preload, c)
|
||||
}
|
||||
*/
|
||||
114
src/server/web/api/torrents.go
Normal file
114
src/server/web/api/torrents.go
Normal file
@@ -0,0 +1,114 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/anacrolix/torrent/metainfo"
|
||||
"github.com/gin-gonic/gin"
|
||||
"server/torr"
|
||||
"server/web/api/utils"
|
||||
)
|
||||
|
||||
//Action: add, get, rem, list
|
||||
type torrReqJS struct {
|
||||
requestI
|
||||
Link string `json:"link,omitempty"`
|
||||
Hash string `json:"hash,omitempty"`
|
||||
Title string `json:"title,omitempty"`
|
||||
Poster string `json:"poster,omitempty"`
|
||||
SaveToDB bool `json:"save_to_db,omitempty"`
|
||||
}
|
||||
|
||||
func torrents(c *gin.Context) {
|
||||
var req torrReqJS
|
||||
err := c.ShouldBindJSON(&req)
|
||||
if err != nil {
|
||||
c.AbortWithError(http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
|
||||
switch req.Action {
|
||||
case "add":
|
||||
{
|
||||
add(req, c)
|
||||
}
|
||||
case "get":
|
||||
{
|
||||
get(req, c)
|
||||
}
|
||||
case "rem":
|
||||
{
|
||||
rem(req, c)
|
||||
}
|
||||
case "list":
|
||||
{
|
||||
list(req, c)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func add(req torrReqJS, c *gin.Context) {
|
||||
torrSpec, err := utils.ParseLink(req.Link)
|
||||
if err != nil {
|
||||
c.AbortWithError(http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
|
||||
torr, err := torr.NewTorrent(torrSpec, bts)
|
||||
if err != nil {
|
||||
c.AbortWithError(http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
torr.Title = req.Title
|
||||
torr.Poster = req.Poster
|
||||
|
||||
if req.SaveToDB {
|
||||
utils.AddTorrent(torr)
|
||||
}
|
||||
|
||||
st := torr.Stats()
|
||||
c.JSON(200, st)
|
||||
}
|
||||
|
||||
func get(req torrReqJS, c *gin.Context) {
|
||||
hash := metainfo.NewHashFromHex(req.Hash)
|
||||
tor := bts.GetTorrent(hash)
|
||||
if tor == nil {
|
||||
tor = utils.GetTorrent(hash)
|
||||
}
|
||||
|
||||
if tor != nil {
|
||||
st := tor.Stats()
|
||||
c.JSON(200, st)
|
||||
} else {
|
||||
c.Status(http.StatusNotFound)
|
||||
}
|
||||
}
|
||||
|
||||
func rem(req torrReqJS, c *gin.Context) {
|
||||
hash := metainfo.NewHashFromHex(req.Hash)
|
||||
bts.RemoveTorrent(hash)
|
||||
utils.RemTorrent(hash)
|
||||
c.Status(200)
|
||||
}
|
||||
|
||||
func list(req torrReqJS, c *gin.Context) {
|
||||
btlist := bts.ListTorrents()
|
||||
dblist := utils.ListTorrents()
|
||||
var stats []torr.TorrentStats
|
||||
for _, tr := range btlist {
|
||||
stats = append(stats, tr.Stats())
|
||||
}
|
||||
|
||||
mainloop:
|
||||
for _, db := range dblist {
|
||||
for _, tr := range btlist {
|
||||
if tr.Hash() == db.Hash() {
|
||||
continue mainloop
|
||||
}
|
||||
}
|
||||
stats = append(stats, db.Stats())
|
||||
}
|
||||
|
||||
c.JSON(200, stats)
|
||||
}
|
||||
50
src/server/web/api/utils/dbwrapper.go
Normal file
50
src/server/web/api/utils/dbwrapper.go
Normal file
@@ -0,0 +1,50 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/anacrolix/torrent/metainfo"
|
||||
"server/settings"
|
||||
"server/torr"
|
||||
)
|
||||
|
||||
func AddTorrent(torr *torr.Torrent) {
|
||||
t := new(settings.TorrentDB)
|
||||
t.TorrentSpec = torr.TorrentSpec
|
||||
t.Title = torr.Title
|
||||
t.Poster = torr.Poster
|
||||
t.Timestamp = time.Now().Unix()
|
||||
t.Files = torr.Stats().FileStats
|
||||
settings.AddTorrent(t)
|
||||
}
|
||||
|
||||
func GetTorrent(hash metainfo.Hash) *torr.Torrent {
|
||||
list := settings.ListTorrent()
|
||||
for _, db := range list {
|
||||
if hash == db.InfoHash {
|
||||
torr := new(torr.Torrent)
|
||||
torr.TorrentSpec = db.TorrentSpec
|
||||
torr.Title = db.Title
|
||||
torr.Poster = db.Poster
|
||||
return torr
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func RemTorrent(hash metainfo.Hash) {
|
||||
settings.RemTorrent(hash)
|
||||
}
|
||||
|
||||
func ListTorrents() []*torr.Torrent {
|
||||
var ret []*torr.Torrent
|
||||
list := settings.ListTorrent()
|
||||
for _, db := range list {
|
||||
torr := new(torr.Torrent)
|
||||
torr.TorrentSpec = db.TorrentSpec
|
||||
torr.Title = db.Title
|
||||
torr.Poster = db.Poster
|
||||
ret = append(ret, torr)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
108
src/server/web/api/utils/link.go
Normal file
108
src/server/web/api/utils/link.go
Normal file
@@ -0,0 +1,108 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/anacrolix/torrent"
|
||||
"github.com/anacrolix/torrent/metainfo"
|
||||
)
|
||||
|
||||
func ParseLink(link string) (*torrent.TorrentSpec, error) {
|
||||
urlLink, err := url.Parse(link)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch strings.ToLower(urlLink.Scheme) {
|
||||
case "magnet":
|
||||
return fromMagnet(urlLink.String())
|
||||
case "http", "https":
|
||||
return fromHttp(urlLink.String())
|
||||
case "":
|
||||
return fromMagnet("magnet:?xt=urn:btih:" + urlLink.Path)
|
||||
case "file":
|
||||
return fromFile(urlLink.Path)
|
||||
default:
|
||||
err = fmt.Errorf("unknown scheme:", urlLink, urlLink.Scheme)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func fromMagnet(link string) (*torrent.TorrentSpec, error) {
|
||||
mag, err := metainfo.ParseMagnetURI(link)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &torrent.TorrentSpec{
|
||||
InfoBytes: nil,
|
||||
Trackers: [][]string{mag.Trackers},
|
||||
DisplayName: mag.DisplayName,
|
||||
InfoHash: mag.InfoHash,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func fromHttp(url string) (*torrent.TorrentSpec, error) {
|
||||
req, err := http.NewRequest("GET", url, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
client := new(http.Client)
|
||||
client.Timeout = time.Duration(time.Second * 30)
|
||||
req.Header.Set("User-Agent", "DWL/1.1.1 (Torrent)")
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode != 200 {
|
||||
return nil, errors.New(resp.Status)
|
||||
}
|
||||
|
||||
minfo, err := metainfo.Load(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
info, err := minfo.UnmarshalInfo()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mag := minfo.Magnet(info.Name, minfo.HashInfoBytes())
|
||||
|
||||
return &torrent.TorrentSpec{
|
||||
InfoBytes: minfo.InfoBytes,
|
||||
Trackers: [][]string{mag.Trackers},
|
||||
DisplayName: info.Name,
|
||||
InfoHash: minfo.HashInfoBytes(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func fromFile(path string) (*torrent.TorrentSpec, error) {
|
||||
if runtime.GOOS == "windows" && strings.HasPrefix(path, "/") {
|
||||
path = strings.TrimPrefix(path, "/")
|
||||
}
|
||||
minfo, err := metainfo.LoadFromFile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
info, err := minfo.UnmarshalInfo()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mag := minfo.Magnet(info.Name, minfo.HashInfoBytes())
|
||||
return &torrent.TorrentSpec{
|
||||
InfoBytes: minfo.InfoBytes,
|
||||
Trackers: [][]string{mag.Trackers},
|
||||
DisplayName: info.Name,
|
||||
InfoHash: minfo.HashInfoBytes(),
|
||||
}, nil
|
||||
}
|
||||
@@ -18,7 +18,7 @@ func Start(port string) {
|
||||
route := gin.New()
|
||||
route.Use(gin.Logger(), gin.Recovery())
|
||||
|
||||
api.SetupRouteApi(route)
|
||||
api.SetupRouteApi(route, BTS)
|
||||
|
||||
waitChan <- route.Run(":" + port)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user