refactor and update

This commit is contained in:
YouROK
2020-11-11 12:48:02 +03:00
parent 1ce27ef121
commit b4a20760cc
16 changed files with 324 additions and 260 deletions

View File

@@ -14,6 +14,7 @@ import (
type TorrentDB struct { type TorrentDB struct {
*torrent.TorrentSpec *torrent.TorrentSpec
Name string `json:"name"`
Title string `json:"title,omitempty"` Title string `json:"title,omitempty"`
Poster string `json:"poster,omitempty"` Poster string `json:"poster,omitempty"`

View File

@@ -12,15 +12,26 @@ type Viewed struct {
} }
func SetViewed(vv *Viewed) { func SetViewed(vv *Viewed) {
var indexes map[int]struct{}
var err error
buf := tdb.Get("Viewed", vv.Hash) buf := tdb.Get("Viewed", vv.Hash)
var indeces map[int]struct{} if len(buf) == 0 {
err := json.Unmarshal(buf, &indeces) indexes = make(map[int]struct{})
if err == nil { indexes[vv.FileIndex] = struct{}{}
indeces[vv.FileIndex] = struct{}{} buf, err = json.Marshal(indexes)
buf, err = json.Marshal(indeces)
if err == nil { if err == nil {
tdb.Set("Viewed", vv.Hash, buf) tdb.Set("Viewed", vv.Hash, buf)
} }
} else {
err = json.Unmarshal(buf, &indexes)
if err == nil {
indexes[vv.FileIndex] = struct{}{}
buf, err = json.Marshal(indexes)
if err == nil {
tdb.Set("Viewed", vv.Hash, buf)
}
}
} }
if err != nil { if err != nil {
log.TLogln("Error set viewed:", err) log.TLogln("Error set viewed:", err)
@@ -65,7 +76,7 @@ func ListViewed(hash string) []*Viewed {
err = json.Unmarshal(buf, &indeces) err = json.Unmarshal(buf, &indeces)
if err == nil { if err == nil {
for i, _ := range indeces { for i, _ := range indeces {
ret = append(ret, &Viewed{hash, i}) ret = append(ret, &Viewed{key, i})
} }
} }
} }

View File

@@ -0,0 +1,88 @@
package torr
import (
"errors"
"sort"
"github.com/anacrolix/torrent"
"github.com/anacrolix/torrent/metainfo"
"server/log"
)
var (
bts *BTServer
)
func InitApiHelper(bt *BTServer) {
bts = bt
}
func AddTorrent(spec *torrent.TorrentSpec, title, poster string) (*Torrent, error) {
torr, err := NewTorrent(spec, bts)
if err != nil {
log.TLogln("error add torrent:", err)
return nil, err
}
if !torr.GotInfo() {
log.TLogln("error add torrent:", "timeout connection torrent")
return nil, errors.New("timeout connection torrent")
}
torr.Title = title
torr.Poster = poster
if torr.Title == "" {
torr.Title = torr.Name()
}
return torr, nil
}
func SaveTorrentToDB(torr *Torrent) {
log.TLogln("save to db:", torr.Hash())
AddTorrentDB(torr)
}
func GetTorrent(hashHex string) *Torrent {
hash := metainfo.NewHashFromHex(hashHex)
tor := bts.GetTorrent(hash)
if tor == nil {
tor = GetTorrentDB(hash)
}
return tor
}
func RemTorrent(hashHex string) {
hash := metainfo.NewHashFromHex(hashHex)
bts.RemoveTorrent(hash)
RemTorrentDB(hash)
}
func ListTorrent() []*Torrent {
btlist := bts.ListTorrents()
dblist := ListTorrentsDB()
for hash, t := range dblist {
if _, ok := btlist[hash]; !ok {
btlist[hash] = t
}
}
var ret []*Torrent
for _, t := range btlist {
ret = append(ret, t)
}
sort.Slice(ret, func(i, j int) bool {
return ret[i].Timestamp > ret[j].Timestamp
})
return ret
}
func DropTorrent(hashHex string) {
hash := metainfo.NewHashFromHex(hashHex)
bts.RemoveTorrent(hash)
}

View File

@@ -36,6 +36,7 @@ func (bt *BTServer) Connect() error {
bt.configure() bt.configure()
bt.client, err = torrent.NewClient(bt.config) bt.client, err = torrent.NewClient(bt.config)
bt.torrents = make(map[metainfo.Hash]*Torrent) bt.torrents = make(map[metainfo.Hash]*Torrent)
InitApiHelper(bt)
return err return err
} }
@@ -109,10 +110,10 @@ func (bt *BTServer) GetTorrent(hash torrent.InfoHash) *Torrent {
return nil return nil
} }
func (bt *BTServer) ListTorrents() []*Torrent { func (bt *BTServer) ListTorrents() map[metainfo.Hash]*Torrent {
var list []*Torrent list := make(map[metainfo.Hash]*Torrent)
for _, t := range bt.torrents { for k, v := range bt.torrents {
list = append(list, t) list[k] = v
} }
return list return list
} }

View File

@@ -1,54 +1,54 @@
package utils package torr
import ( import (
"time" "time"
"server/settings" "server/settings"
"server/torr"
"server/torr/state" "server/torr/state"
"github.com/anacrolix/torrent/metainfo" "github.com/anacrolix/torrent/metainfo"
) )
func AddTorrent(torr *torr.Torrent) { func AddTorrentDB(torr *Torrent) {
t := new(settings.TorrentDB) t := new(settings.TorrentDB)
t.TorrentSpec = torr.TorrentSpec t.TorrentSpec = torr.TorrentSpec
t.Name = torr.Name()
t.Title = torr.Title t.Title = torr.Title
t.Poster = torr.Poster t.Poster = torr.Poster
t.Timestamp = time.Now().Unix() t.Timestamp = time.Now().Unix()
t.Files = torr.Stats().FileStats t.Files = torr.Status().FileStats
settings.AddTorrent(t) settings.AddTorrent(t)
} }
func GetTorrent(hash metainfo.Hash) *torr.Torrent { func GetTorrentDB(hash metainfo.Hash) *Torrent {
list := settings.ListTorrent() list := settings.ListTorrent()
for _, db := range list { for _, db := range list {
if hash == db.InfoHash { if hash == db.InfoHash {
torr := new(torr.Torrent) torr := new(Torrent)
torr.TorrentSpec = db.TorrentSpec torr.TorrentSpec = db.TorrentSpec
torr.Title = db.Title torr.Title = db.Title
torr.Poster = db.Poster torr.Poster = db.Poster
torr.Status = state.TorrentInDB torr.Stat = state.TorrentInDB
return torr return torr
} }
} }
return nil return nil
} }
func RemTorrent(hash metainfo.Hash) { func RemTorrentDB(hash metainfo.Hash) {
settings.RemTorrent(hash) settings.RemTorrent(hash)
} }
func ListTorrents() []*torr.Torrent { func ListTorrentsDB() map[metainfo.Hash]*Torrent {
var ret []*torr.Torrent ret := make(map[metainfo.Hash]*Torrent)
list := settings.ListTorrent() list := settings.ListTorrent()
for _, db := range list { for _, db := range list {
torr := new(torr.Torrent) torr := new(Torrent)
torr.TorrentSpec = db.TorrentSpec torr.TorrentSpec = db.TorrentSpec
torr.Title = db.Title torr.Title = db.Title
torr.Poster = db.Poster torr.Poster = db.Poster
torr.Status = state.TorrentInDB torr.Stat = state.TorrentInDB
ret = append(ret, torr) ret[torr.TorrentSpec.InfoHash] = torr
} }
return ret return ret
} }

View File

@@ -1,8 +1,8 @@
package state package state
type TorrentStatus int type TorrentStat int
func (t TorrentStatus) String() string { func (t TorrentStat) String() string {
switch t { switch t {
case TorrentAdded: case TorrentAdded:
return "Torrent added" return "Torrent added"
@@ -22,7 +22,7 @@ func (t TorrentStatus) String() string {
} }
const ( const (
TorrentAdded = TorrentStatus(iota) TorrentAdded = TorrentStat(iota)
TorrentGettingInfo TorrentGettingInfo
TorrentPreload TorrentPreload
TorrentWorking TorrentWorking
@@ -30,15 +30,16 @@ const (
TorrentInDB TorrentInDB
) )
type TorrentStats struct { type TorrentStatus struct {
Title string `json:"title"` Title string `json:"title"`
Poster string `json:"poster"` Poster string `json:"poster"`
Timestamp int64 `json:"timestamp"`
Name string `json:"name,omitempty"` Name string `json:"name,omitempty"`
Hash string `json:"hash,omitempty"` Hash string `json:"hash,omitempty"`
TorrentStatus TorrentStatus `json:"torrent_status,omitempty"` Stat TorrentStat `json:"stat"`
TorrentStatusString string `json:"torrent_status_string,omitempty"` StatString string `json:"stat_string"`
LoadedSize int64 `json:"loaded_size,omitempty"` LoadedSize int64 `json:"loaded_size,omitempty"`
TorrentSize int64 `json:"torrent_size,omitempty"` TorrentSize int64 `json:"torrent_size,omitempty"`

View File

@@ -8,6 +8,7 @@ import (
"time" "time"
"github.com/anacrolix/missinggo/httptoo" "github.com/anacrolix/missinggo/httptoo"
sets "server/settings"
) )
func (t *Torrent) Stream(fileIndex int, req *http.Request, resp http.ResponseWriter) error { func (t *Torrent) Stream(fileIndex int, req *http.Request, resp http.ResponseWriter) error {
@@ -20,6 +21,8 @@ func (t *Torrent) Stream(fileIndex int, req *http.Request, resp http.ResponseWri
log.Println("Connect client") log.Println("Connect client")
sets.SetViewed(&sets.Viewed{t.Hash().HexString(), fileIndex})
resp.Header().Set("Connection", "close") resp.Header().Set("Connection", "close")
resp.Header().Set("ETag", httptoo.EncodeQuotedString(fmt.Sprintf("%s/%s", t.Hash().HexString(), file.Path()))) resp.Header().Set("ETag", httptoo.EncodeQuotedString(fmt.Sprintf("%s/%s", t.Hash().HexString(), file.Path())))

View File

@@ -24,7 +24,8 @@ type Torrent struct {
Poster string Poster string
*torrent.TorrentSpec *torrent.TorrentSpec
Status state.TorrentStatus Stat state.TorrentStat
Timestamp int64
///// /////
*torrent.Torrent *torrent.Torrent
@@ -50,7 +51,6 @@ type Torrent struct {
} }
func NewTorrent(spec *torrent.TorrentSpec, bt *BTServer) (*Torrent, error) { func NewTorrent(spec *torrent.TorrentSpec, bt *BTServer) (*Torrent, error) {
switch settings.BTsets.RetrackersMode { switch settings.BTsets.RetrackersMode {
case 1: case 1:
spec.Trackers = append(spec.Trackers, [][]string{utils.GetDefTrackers()}...) spec.Trackers = append(spec.Trackers, [][]string{utils.GetDefTrackers()}...)
@@ -73,12 +73,13 @@ func NewTorrent(spec *torrent.TorrentSpec, bt *BTServer) (*Torrent, error) {
torr := new(Torrent) torr := new(Torrent)
torr.Torrent = goTorrent torr.Torrent = goTorrent
torr.Status = state.TorrentAdded torr.Stat = state.TorrentAdded
torr.lastTimeSpeed = time.Now() torr.lastTimeSpeed = time.Now()
torr.bt = bt torr.bt = bt
torr.closed = goTorrent.Closed() torr.closed = goTorrent.Closed()
torr.TorrentSpec = spec torr.TorrentSpec = spec
torr.expiredTime = time.Now().Add(time.Minute) torr.expiredTime = time.Now().Add(time.Minute)
torr.Timestamp = time.Now().Unix()
go torr.watch() go torr.watch()
@@ -106,12 +107,12 @@ func (t *Torrent) WaitInfo() bool {
} }
func (t *Torrent) GotInfo() bool { func (t *Torrent) GotInfo() bool {
if t.Status == state.TorrentClosed { if t.Stat == state.TorrentClosed {
return false return false
} }
t.Status = state.TorrentGettingInfo t.Stat = state.TorrentGettingInfo
if t.WaitInfo() { if t.WaitInfo() {
t.Status = state.TorrentWorking t.Stat = state.TorrentWorking
t.expiredTime = time.Now().Add(time.Minute * 5) t.expiredTime = time.Now().Add(time.Minute * 5)
return true return true
} else { } else {
@@ -179,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 == state.TorrentWorking || t.Status == state.TorrentClosed) return t.cache.ReadersLen() == 0 && t.expiredTime.Before(time.Now()) && (t.Stat == state.TorrentWorking || t.Stat == state.TorrentClosed)
} }
func (t *Torrent) Files() []*torrent.File { func (t *Torrent) Files() []*torrent.File {
@@ -208,7 +209,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 == state.TorrentClosed { if t.Stat == state.TorrentClosed {
return nil return nil
} }
reader := NewReader(t, file, readahead) reader := NewReader(t, file, readahead)
@@ -230,14 +231,14 @@ func (t *Torrent) Preload(index int, size int64) {
return return
} }
if t.Status == state.TorrentGettingInfo { if t.Stat == 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 != state.TorrentWorking { if t.Stat != state.TorrentWorking {
t.muTorrent.Unlock() t.muTorrent.Unlock()
return return
} }
@@ -249,12 +250,12 @@ func (t *Torrent) Preload(index int, size int64) {
t.muTorrent.Unlock() t.muTorrent.Unlock()
return return
} }
t.Status = state.TorrentPreload t.Stat = state.TorrentPreload
t.muTorrent.Unlock() t.muTorrent.Unlock()
defer func() { defer func() {
if t.Status == state.TorrentPreload { if t.Stat == state.TorrentPreload {
t.Status = state.TorrentWorking t.Stat = state.TorrentWorking
} }
}() }()
@@ -299,7 +300,7 @@ 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 == state.TorrentPreload { for t.Stat == 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.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) 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)
@@ -330,7 +331,7 @@ func (t *Torrent) drop() {
} }
func (t *Torrent) Close() { func (t *Torrent) Close() {
t.Status = state.TorrentClosed t.Stat = state.TorrentClosed
t.bt.mu.Lock() t.bt.mu.Lock()
defer t.bt.mu.Unlock() defer t.bt.mu.Unlock()
@@ -341,16 +342,17 @@ func (t *Torrent) Close() {
t.drop() t.drop()
} }
func (t *Torrent) Stats() *state.TorrentStats { func (t *Torrent) Status() *state.TorrentStatus {
t.muTorrent.Lock() t.muTorrent.Lock()
defer t.muTorrent.Unlock() defer t.muTorrent.Unlock()
st := new(state.TorrentStats) st := new(state.TorrentStatus)
st.TorrentStatus = t.Status st.Stat = t.Stat
st.TorrentStatusString = t.Status.String() st.StatString = t.Stat.String()
st.Title = t.Title st.Title = t.Title
st.Poster = t.Poster st.Poster = t.Poster
st.Timestamp = t.Timestamp
if t.TorrentSpec != nil { if t.TorrentSpec != nil {
st.Hash = t.TorrentSpec.InfoHash.HexString() st.Hash = t.TorrentSpec.InfoHash.HexString()

View File

@@ -78,7 +78,7 @@ func GetMimeType(filename string) string {
return "*/*" return "*/*"
} }
func GetPlayableFiles(st state.TorrentStats) []state.TorrentFileStat { func GetPlayableFiles(st state.TorrentStatus) []state.TorrentFileStat {
files := make([]state.TorrentFileStat, 0) files := make([]state.TorrentFileStat, 0)
for _, f := range st.FileStats { for _, f := range st.FileStats {
if GetMimeType(f.Path) != "*/*" { if GetMimeType(f.Path) != "*/*" {

View File

@@ -8,7 +8,9 @@ import (
"path/filepath" "path/filepath"
"time" "time"
"github.com/anacrolix/missinggo/httptoo"
sets "server/settings" sets "server/settings"
"server/torr"
"server/torr/state" "server/torr/state"
"server/utils" "server/utils"
@@ -18,21 +20,17 @@ import (
func allPlayList(c *gin.Context) { func allPlayList(c *gin.Context) {
_, fromlast := c.GetQuery("fromlast") _, fromlast := c.GetQuery("fromlast")
stats := listTorrents() torrs := torr.ListTorrent()
host := "http://" + c.Request.Host host := "http://" + c.Request.Host
list := "#EXTM3U\n" list := "#EXTM3U\n"
hash := ""
for _, stat := range stats { for _, tr := range torrs {
list += getM3uList(stat, host, fromlast) list += getM3uList(tr.Status(), host, fromlast)
hash += tr.Hash().HexString()
} }
c.Header("Content-Type", "audio/x-mpegurl") sendM3U(c, "all.m3u", hash, list)
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) { func playList(c *gin.Context) {
@@ -43,34 +41,43 @@ func playList(c *gin.Context) {
return return
} }
stats := listTorrents() tors := torr.ListTorrent()
var stat *state.TorrentStats var tor *torr.Torrent
for _, st := range stats { for _, tr := range tors {
if st.Hash == hash { if tr.Hash().HexString() == hash {
stat = st tor = tr
break break
} }
} }
if stat == nil { if tor == nil {
c.AbortWithStatus(http.StatusNotFound) c.AbortWithStatus(http.StatusNotFound)
return return
} }
// TODO проверить // TODO проверить
host := "http://" + c.Request.Host host := "http://" + c.Request.Host
list := getM3uList(stat, host, fromlast) list := getM3uList(tor.Status(), host, fromlast)
list = "#EXTM3U\n" + list list = "#EXTM3U\n" + list
sendM3U(c, tor.Name()+".m3u", tor.Hash().HexString(), list)
}
func sendM3U(c *gin.Context, name, hash string, m3u string) {
c.Header("Content-Type", "audio/x-mpegurl") c.Header("Content-Type", "audio/x-mpegurl")
c.Header("Connection", "close") c.Header("Connection", "close")
c.Header("Content-Disposition", `attachment; filename="playlist.m3u"`) if hash != "" {
http.ServeContent(c.Writer, c.Request, "playlist.m3u", time.Now(), bytes.NewReader([]byte(list))) c.Header("ETag", httptoo.EncodeQuotedString(fmt.Sprintf("%s/%s", hash, name)))
}
if name == "" {
name = "playlist.m3u"
}
c.Header("Content-Disposition", `attachment; filename="`+name+`"`)
http.ServeContent(c.Writer, c.Request, name, time.Now(), bytes.NewReader([]byte(m3u)))
c.Status(200) c.Status(200)
} }
func getM3uList(tor *state.TorrentStats, host string, fromLast bool) string { func getM3uList(tor *state.TorrentStatus, host string, fromLast bool) string {
m3u := "" m3u := ""
from := 0 from := 0
if fromLast { if fromLast {
@@ -87,15 +94,20 @@ func getM3uList(tor *state.TorrentStats, host string, fromLast bool) string {
fn = f.Path fn = f.Path
} }
m3u += "#EXTINF:0," + fn + "\n" m3u += "#EXTINF:0," + fn + "\n"
// http://127.0.0.1:8090/stream/fname?link=...&index=0&play title := filepath.Base(f.Path)
m3u += host + "/stream/" + url.QueryEscape(f.Path) + "?link=" + tor.Hash + "&file=" + fmt.Sprint(f.Id) + "\n" if tor.Title != "" {
title = tor.Title
} else if tor.Name != "" {
title = tor.Name
}
m3u += host + "/stream/" + url.PathEscape(title) + "?link=" + tor.Hash + "&index=" + fmt.Sprint(f.Id) + "&play\n"
} }
} }
} }
return m3u return m3u
} }
func searchLastPlayed(tor *state.TorrentStats) int { func searchLastPlayed(tor *state.TorrentStatus) int {
//TODO проверить //TODO проверить
viewed := sets.ListViewed(tor.Hash) viewed := sets.ListViewed(tor.Hash)
for i := len(tor.FileStats); i > 0; i-- { for i := len(tor.FileStats); i > 0; i-- {

View File

@@ -22,6 +22,7 @@ func SetupRouteApi(route *gin.Engine, serv *torr.BTServer) {
route.POST("/settings", settings) route.POST("/settings", settings)
route.POST("/torrents", torrents) route.POST("/torrents", torrents)
route.POST("/torrent/upload", torrentUpload)
route.GET("/stream", stream) route.GET("/stream", stream)
route.GET("/stream/*fname", stream) route.GET("/stream/*fname", stream)

View File

@@ -4,6 +4,7 @@ import (
"net/http" "net/http"
"net/url" "net/url"
"strconv" "strconv"
"strings"
"server/torr" "server/torr"
"server/web/api/utils" "server/web/api/utils"
@@ -12,12 +13,17 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
) )
// http://127.0.0.1:8090/stream/fname?link=...&index=1&stat // get stat
// http://127.0.0.1:8090/stream/fname?link=...&stat
// get m3u
// http://127.0.0.1:8090/stream/fname?link=...&index=1&m3u // http://127.0.0.1:8090/stream/fname?link=...&index=1&m3u
// http://127.0.0.1:8090/stream/fname?link=...&index=1&m3u&fromlast
// stream torrent
// http://127.0.0.1:8090/stream/fname?link=...&index=1&play // http://127.0.0.1:8090/stream/fname?link=...&index=1&play
// http://127.0.0.1:8090/stream/fname?link=...&save&title=...&poster=...
// http://127.0.0.1:8090/stream/fname?link=...&index=1&play&save // http://127.0.0.1:8090/stream/fname?link=...&index=1&play&save
// http://127.0.0.1:8090/stream/fname?link=...&index=1&play&save&title=...&poster=... // http://127.0.0.1:8090/stream/fname?link=...&index=1&play&save&title=...&poster=...
// only save
// http://127.0.0.1:8090/stream/fname?link=...&save&title=...&poster=...
func stream(c *gin.Context) { func stream(c *gin.Context) {
link := c.Query("link") link := c.Query("link")
@@ -26,6 +32,7 @@ func stream(c *gin.Context) {
_, stat := c.GetQuery("stat") _, stat := c.GetQuery("stat")
_, save := c.GetQuery("save") _, save := c.GetQuery("save")
_, m3u := c.GetQuery("m3u") _, m3u := c.GetQuery("m3u")
_, fromlast := c.GetQuery("fromlast")
_, play := c.GetQuery("play") _, play := c.GetQuery("play")
title := c.Query("title") title := c.Query("title")
poster := c.Query("poster") poster := c.Query("poster")
@@ -37,10 +44,13 @@ func stream(c *gin.Context) {
if title == "" { if title == "" {
title = c.Param("fname") title = c.Param("fname")
title, _ = url.PathUnescape(title)
title = strings.TrimLeft(title, "/")
} else {
title, _ = url.QueryUnescape(title)
} }
link, _ = url.QueryUnescape(link) link, _ = url.QueryUnescape(link)
title, _ = url.QueryUnescape(title)
poster, _ = url.QueryUnescape(poster) poster, _ = url.QueryUnescape(poster)
spec, err := utils.ParseLink(link) spec, err := utils.ParseLink(link)
@@ -49,43 +59,16 @@ func stream(c *gin.Context) {
return return
} }
var tor *torr.Torrent tor, err := torr.AddTorrent(spec, title, poster)
if err != nil {
// find torrent in bts c.AbortWithError(http.StatusInternalServerError, err)
for _, torrent := range bts.ListTorrents() { return
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
}
}
tor.Title = title
tor.Poster = poster
// save to db // save to db
if save { if save {
utils.AddTorrent(tor) torr.SaveTorrentToDB(tor)
c.Status(200) c.Status(200) // only set status, not return
}
// wait torrent info
if !tor.GotInfo() {
c.AbortWithError(http.StatusInternalServerError, errors.New("timeout torrent get info"))
return
} }
// find file // find file
@@ -98,7 +81,7 @@ func stream(c *gin.Context) {
index = ind index = ind
} }
} }
if index == -1 { if index == -1 && play { // if file index not set and play file exec
c.AbortWithError(http.StatusBadRequest, errors.New("\"index\" is empty or wrong")) c.AbortWithError(http.StatusBadRequest, errors.New("\"index\" is empty or wrong"))
return return
} }
@@ -107,14 +90,14 @@ func stream(c *gin.Context) {
tor.Preload(index, 0) tor.Preload(index, 0)
} }
// return stat if query // return stat if query
if stat || (!m3u && !play) { if stat {
c.JSON(200, tor.Stats()) c.JSON(200, tor.Status())
return return
} else } else
// return m3u if query // return m3u if query
if m3u { if m3u {
//TODO m3u m3ulist := "#EXTM3U\n" + getM3uList(tor.Status(), "http://"+c.Request.Host, fromlast)
c.JSON(200, tor.Stats()) sendM3U(c, tor.Name(), tor.Hash().HexString(), m3ulist)
return return
} else } else
// return play if query // return play if query
@@ -123,96 +106,3 @@ func stream(c *gin.Context) {
return 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

@@ -8,7 +8,6 @@ import (
"server/torr/state" "server/torr/state"
"server/web/api/utils" "server/web/api/utils"
"github.com/anacrolix/torrent/metainfo"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
@@ -56,6 +55,11 @@ func torrents(c *gin.Context) {
} }
func addTorrent(req torrReqJS, c *gin.Context) { func addTorrent(req torrReqJS, c *gin.Context) {
if req.Link == "" {
c.AbortWithError(http.StatusBadRequest, errors.New("link is empty"))
return
}
log.TLogln("add torrent", req.Link) log.TLogln("add torrent", req.Link)
torrSpec, err := utils.ParseLink(req.Link) torrSpec, err := utils.ParseLink(req.Link)
if err != nil { if err != nil {
@@ -64,44 +68,30 @@ func addTorrent(req torrReqJS, c *gin.Context) {
return return
} }
torr, err := torr.NewTorrent(torrSpec, bts) tor, err := torr.AddTorrent(torrSpec, req.Title, req.Poster)
if err != nil { if err != nil {
log.TLogln("error add torrent:", err) log.TLogln("error add torrent:", err)
c.AbortWithError(http.StatusInternalServerError, err) c.AbortWithError(http.StatusInternalServerError, err)
return return
} }
if !torr.GotInfo() {
log.TLogln("error add torrent:", "timeout connection torrent")
c.AbortWithError(http.StatusNotFound, errors.New("timeout connection torrent"))
return
}
torr.Title = req.Title
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()) torr.SaveTorrentToDB(tor)
utils.AddTorrent(torr)
} }
st := torr.Stats() st := tor.Status()
c.JSON(200, st) c.JSON(200, st)
} }
func getTorrent(req torrReqJS, c *gin.Context) { func getTorrent(req torrReqJS, c *gin.Context) {
hash := metainfo.NewHashFromHex(req.Hash) if req.Hash == "" {
tor := bts.GetTorrent(hash) c.AbortWithError(http.StatusBadRequest, errors.New("hash is empty"))
if tor == nil { return
tor = utils.GetTorrent(hash)
} }
tor := torr.GetTorrent(req.Hash)
if tor != nil { if tor != nil {
st := tor.Stats() st := tor.Status()
c.JSON(200, st) c.JSON(200, st)
} else { } else {
c.Status(http.StatusNotFound) c.Status(http.StatusNotFound)
@@ -109,44 +99,28 @@ func getTorrent(req torrReqJS, c *gin.Context) {
} }
func remTorrent(req torrReqJS, c *gin.Context) { func remTorrent(req torrReqJS, c *gin.Context) {
hash := metainfo.NewHashFromHex(req.Hash) if req.Hash == "" {
bts.RemoveTorrent(hash) c.AbortWithError(http.StatusBadRequest, errors.New("hash is empty"))
utils.RemTorrent(hash) return
}
torr.RemTorrent(req.Hash)
c.Status(200) c.Status(200)
} }
func listTorrent(req torrReqJS, c *gin.Context) { func listTorrent(req torrReqJS, c *gin.Context) {
stats := listTorrents() list := torr.ListTorrent()
var stats []*state.TorrentStatus
for _, tr := range list {
stats = append(stats, tr.Status())
}
c.JSON(200, stats) c.JSON(200, stats)
} }
func listTorrents() []*state.TorrentStats {
btlist := bts.ListTorrents()
dblist := utils.ListTorrents()
var stats []*state.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())
}
return stats
}
func dropTorrent(req torrReqJS, c *gin.Context) { func dropTorrent(req torrReqJS, c *gin.Context) {
if req.Hash == "" { if req.Hash == "" {
c.AbortWithError(http.StatusBadRequest, errors.New("hash is empty")) c.AbortWithError(http.StatusBadRequest, errors.New("hash is empty"))
return return
} }
hash := metainfo.NewHashFromHex(req.Hash) torr.DropTorrent(req.Hash)
bts.RemoveTorrent(hash)
c.Status(200) c.Status(200)
} }

View File

@@ -0,0 +1,51 @@
package api
import (
"net/http"
"github.com/gin-gonic/gin"
"server/log"
"server/torr"
"server/torr/state"
"server/web/api/utils"
)
func torrentUpload(c *gin.Context) {
form, err := c.MultipartForm()
if err != nil {
c.AbortWithError(http.StatusBadRequest, err)
return
}
defer form.RemoveAll()
save := len(form.Value["save"]) > 0
var retList []*state.TorrentStatus
for name, file := range form.File {
log.TLogln("add torrent file", name)
torrFile, err := file[0].Open()
if err != nil {
log.TLogln("error upload torrent:", err)
continue
}
defer torrFile.Close()
spec, err := utils.ParseFile(torrFile)
if err != nil {
log.TLogln("error upload torrent:", err)
continue
}
tor, err := torr.AddTorrent(spec, "", "")
if err != nil {
log.TLogln("error upload torrent:", err)
continue
}
if save {
torr.SaveTorrentToDB(tor)
}
retList = append(retList, tor.Status())
}
c.JSON(200, retList)
}

View File

@@ -3,6 +3,7 @@ package utils
import ( import (
"errors" "errors"
"fmt" "fmt"
"mime/multipart"
"net/http" "net/http"
"net/url" "net/url"
"runtime" "runtime"
@@ -13,6 +14,25 @@ import (
"github.com/anacrolix/torrent/metainfo" "github.com/anacrolix/torrent/metainfo"
) )
func ParseFile(file multipart.File) (*torrent.TorrentSpec, error) {
minfo, err := metainfo.Load(file)
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 ParseLink(link string) (*torrent.TorrentSpec, error) { func ParseLink(link string) (*torrent.TorrentSpec, error) {
urlLink, err := url.Parse(link) urlLink, err := url.Parse(link)
if err != nil { if err != nil {
@@ -40,9 +60,14 @@ func fromMagnet(link string) (*torrent.TorrentSpec, error) {
return nil, err return nil, err
} }
var trackers [][]string
if len(mag.Trackers) > 0 {
trackers = [][]string{mag.Trackers}
}
return &torrent.TorrentSpec{ return &torrent.TorrentSpec{
InfoBytes: nil, InfoBytes: nil,
Trackers: [][]string{mag.Trackers}, Trackers: trackers,
DisplayName: mag.DisplayName, DisplayName: mag.DisplayName,
InfoHash: mag.InfoHash, InfoHash: mag.InfoHash,
}, nil }, nil

View File

@@ -7,6 +7,10 @@ import (
sets "server/settings" sets "server/settings"
) )
/*
file index starts from 1
*/
// Action: set, rem, list // Action: set, rem, list
type viewedReqJS struct { type viewedReqJS struct {
requestI requestI