diff --git a/src/server/settings/torrent.go b/src/server/settings/torrent.go new file mode 100644 index 0000000..f9c5898 --- /dev/null +++ b/src/server/settings/torrent.go @@ -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() +} diff --git a/src/server/torr/btserver.go b/src/server/torr/btserver.go index 8912ab8..bc9c21b 100644 --- a/src/server/torr/btserver.go +++ b/src/server/torr/btserver.go @@ -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() diff --git a/src/server/torr/state.go b/src/server/torr/state.go index 6dc225e..ae6e259 100644 --- a/src/server/torr/state.go +++ b/src/server/torr/state.go @@ -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() diff --git a/src/server/torr/torrent.go b/src/server/torr/torrent.go index 8d78c09..28c0d78 100644 --- a/src/server/torr/torrent.go +++ b/src/server/torr/torrent.go @@ -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() diff --git a/src/server/web/api/doc.go b/src/server/web/api/doc.go index 2911f4e..fec1561 100644 --- a/src/server/web/api/doc.go +++ b/src/server/web/api/doc.go @@ -3,6 +3,13 @@ package api /* API +respnose JSON{ + +} +request JSON{ + Action string +} + echo version { diff --git a/src/server/web/api/route.go b/src/server/web/api/route.go new file mode 100644 index 0000000..0253384 --- /dev/null +++ b/src/server/web/api/route.go @@ -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) +} diff --git a/src/server/web/api/settings.go b/src/server/web/api/settings.go new file mode 100644 index 0000000..ab558c0 --- /dev/null +++ b/src/server/web/api/settings.go @@ -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")) +} diff --git a/src/server/web/api/stream.go b/src/server/web/api/stream.go new file mode 100644 index 0000000..0824ac6 --- /dev/null +++ b/src/server/web/api/stream.go @@ -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) +} +*/ diff --git a/src/server/web/api/torrents.go b/src/server/web/api/torrents.go new file mode 100644 index 0000000..08f861e --- /dev/null +++ b/src/server/web/api/torrents.go @@ -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) +} diff --git a/src/server/web/api/utils/dbwrapper.go b/src/server/web/api/utils/dbwrapper.go new file mode 100644 index 0000000..ba4bc1d --- /dev/null +++ b/src/server/web/api/utils/dbwrapper.go @@ -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 +} diff --git a/src/server/web/api/utils/link.go b/src/server/web/api/utils/link.go new file mode 100644 index 0000000..3c4ed2c --- /dev/null +++ b/src/server/web/api/utils/link.go @@ -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 +} diff --git a/src/server/web/server.go b/src/server/web/server.go index 8fc543c..9b5fc60 100644 --- a/src/server/web/server.go +++ b/src/server/web/server.go @@ -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) }