creating...

This commit is contained in:
YouROK
2020-11-09 16:45:06 +03:00
parent 0b09da2490
commit 1d4acc1406
12 changed files with 710 additions and 55 deletions

View File

@@ -3,6 +3,13 @@ package api
/*
API
respnose JSON{
}
request JSON{
Action string
}
echo version
{

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

View 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"))
}

View 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)
}
*/

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

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

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