From b4a20760ccfd132951cecfdafb5db4bf1926fbd0 Mon Sep 17 00:00:00 2001 From: YouROK <8yourok8@mail.ru> Date: Wed, 11 Nov 2020 12:48:02 +0300 Subject: [PATCH] refactor and update --- src/server/settings/torrent.go | 1 + src/server/settings/viewed.go | 23 ++- src/server/torr/apihelper.go | 88 ++++++++++ src/server/torr/btserver.go | 9 +- .../{web/api/utils => torr}/dbwrapper.go | 26 +-- src/server/torr/state/state.go | 17 +- src/server/torr/stream.go | 3 + src/server/torr/torrent.go | 40 ++--- src/server/utils/filetypes.go | 2 +- src/server/web/api/m3u.go | 60 ++++--- src/server/web/api/route.go | 1 + src/server/web/api/stream.go | 158 +++--------------- src/server/web/api/torrents.go | 74 +++----- src/server/web/api/upload.go | 51 ++++++ src/server/web/api/utils/link.go | 27 ++- src/server/web/api/viewed.go | 4 + 16 files changed, 324 insertions(+), 260 deletions(-) create mode 100644 src/server/torr/apihelper.go rename src/server/{web/api/utils => torr}/dbwrapper.go (60%) create mode 100644 src/server/web/api/upload.go diff --git a/src/server/settings/torrent.go b/src/server/settings/torrent.go index 529fee0..9092146 100644 --- a/src/server/settings/torrent.go +++ b/src/server/settings/torrent.go @@ -14,6 +14,7 @@ import ( type TorrentDB struct { *torrent.TorrentSpec + Name string `json:"name"` Title string `json:"title,omitempty"` Poster string `json:"poster,omitempty"` diff --git a/src/server/settings/viewed.go b/src/server/settings/viewed.go index f8acb23..bcf707e 100644 --- a/src/server/settings/viewed.go +++ b/src/server/settings/viewed.go @@ -12,15 +12,26 @@ type Viewed struct { } func SetViewed(vv *Viewed) { + var indexes map[int]struct{} + var err error + buf := tdb.Get("Viewed", vv.Hash) - var indeces map[int]struct{} - err := json.Unmarshal(buf, &indeces) - if err == nil { - indeces[vv.FileIndex] = struct{}{} - buf, err = json.Marshal(indeces) + if len(buf) == 0 { + indexes = make(map[int]struct{}) + indexes[vv.FileIndex] = struct{}{} + buf, err = json.Marshal(indexes) if err == nil { 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 { log.TLogln("Error set viewed:", err) @@ -65,7 +76,7 @@ func ListViewed(hash string) []*Viewed { err = json.Unmarshal(buf, &indeces) if err == nil { for i, _ := range indeces { - ret = append(ret, &Viewed{hash, i}) + ret = append(ret, &Viewed{key, i}) } } } diff --git a/src/server/torr/apihelper.go b/src/server/torr/apihelper.go new file mode 100644 index 0000000..39ec3ee --- /dev/null +++ b/src/server/torr/apihelper.go @@ -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) +} diff --git a/src/server/torr/btserver.go b/src/server/torr/btserver.go index b8776b1..4ef6b3a 100644 --- a/src/server/torr/btserver.go +++ b/src/server/torr/btserver.go @@ -36,6 +36,7 @@ func (bt *BTServer) Connect() error { bt.configure() bt.client, err = torrent.NewClient(bt.config) bt.torrents = make(map[metainfo.Hash]*Torrent) + InitApiHelper(bt) return err } @@ -109,10 +110,10 @@ func (bt *BTServer) GetTorrent(hash torrent.InfoHash) *Torrent { return nil } -func (bt *BTServer) ListTorrents() []*Torrent { - var list []*Torrent - for _, t := range bt.torrents { - list = append(list, t) +func (bt *BTServer) ListTorrents() map[metainfo.Hash]*Torrent { + list := make(map[metainfo.Hash]*Torrent) + for k, v := range bt.torrents { + list[k] = v } return list } diff --git a/src/server/web/api/utils/dbwrapper.go b/src/server/torr/dbwrapper.go similarity index 60% rename from src/server/web/api/utils/dbwrapper.go rename to src/server/torr/dbwrapper.go index 8675c9a..9e52575 100644 --- a/src/server/web/api/utils/dbwrapper.go +++ b/src/server/torr/dbwrapper.go @@ -1,54 +1,54 @@ -package utils +package torr import ( "time" "server/settings" - "server/torr" "server/torr/state" "github.com/anacrolix/torrent/metainfo" ) -func AddTorrent(torr *torr.Torrent) { +func AddTorrentDB(torr *Torrent) { t := new(settings.TorrentDB) t.TorrentSpec = torr.TorrentSpec + t.Name = torr.Name() t.Title = torr.Title t.Poster = torr.Poster t.Timestamp = time.Now().Unix() - t.Files = torr.Stats().FileStats + t.Files = torr.Status().FileStats settings.AddTorrent(t) } -func GetTorrent(hash metainfo.Hash) *torr.Torrent { +func GetTorrentDB(hash metainfo.Hash) *Torrent { list := settings.ListTorrent() for _, db := range list { if hash == db.InfoHash { - torr := new(torr.Torrent) + torr := new(Torrent) torr.TorrentSpec = db.TorrentSpec torr.Title = db.Title torr.Poster = db.Poster - torr.Status = state.TorrentInDB + torr.Stat = state.TorrentInDB return torr } } return nil } -func RemTorrent(hash metainfo.Hash) { +func RemTorrentDB(hash metainfo.Hash) { settings.RemTorrent(hash) } -func ListTorrents() []*torr.Torrent { - var ret []*torr.Torrent +func ListTorrentsDB() map[metainfo.Hash]*Torrent { + ret := make(map[metainfo.Hash]*Torrent) list := settings.ListTorrent() for _, db := range list { - torr := new(torr.Torrent) + torr := new(Torrent) torr.TorrentSpec = db.TorrentSpec torr.Title = db.Title torr.Poster = db.Poster - torr.Status = state.TorrentInDB - ret = append(ret, torr) + torr.Stat = state.TorrentInDB + ret[torr.TorrentSpec.InfoHash] = torr } return ret } diff --git a/src/server/torr/state/state.go b/src/server/torr/state/state.go index b855f5a..31208be 100644 --- a/src/server/torr/state/state.go +++ b/src/server/torr/state/state.go @@ -1,8 +1,8 @@ package state -type TorrentStatus int +type TorrentStat int -func (t TorrentStatus) String() string { +func (t TorrentStat) String() string { switch t { case TorrentAdded: return "Torrent added" @@ -22,7 +22,7 @@ func (t TorrentStatus) String() string { } const ( - TorrentAdded = TorrentStatus(iota) + TorrentAdded = TorrentStat(iota) TorrentGettingInfo TorrentPreload TorrentWorking @@ -30,15 +30,16 @@ const ( TorrentInDB ) -type TorrentStats struct { - Title string `json:"title"` - Poster string `json:"poster"` +type TorrentStatus struct { + Title string `json:"title"` + Poster string `json:"poster"` + Timestamp int64 `json:"timestamp"` Name string `json:"name,omitempty"` Hash string `json:"hash,omitempty"` - TorrentStatus TorrentStatus `json:"torrent_status,omitempty"` - TorrentStatusString string `json:"torrent_status_string,omitempty"` + Stat TorrentStat `json:"stat"` + StatString string `json:"stat_string"` LoadedSize int64 `json:"loaded_size,omitempty"` TorrentSize int64 `json:"torrent_size,omitempty"` diff --git a/src/server/torr/stream.go b/src/server/torr/stream.go index b941a68..39d7360 100644 --- a/src/server/torr/stream.go +++ b/src/server/torr/stream.go @@ -8,6 +8,7 @@ import ( "time" "github.com/anacrolix/missinggo/httptoo" + sets "server/settings" ) 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") + sets.SetViewed(&sets.Viewed{t.Hash().HexString(), fileIndex}) + resp.Header().Set("Connection", "close") resp.Header().Set("ETag", httptoo.EncodeQuotedString(fmt.Sprintf("%s/%s", t.Hash().HexString(), file.Path()))) diff --git a/src/server/torr/torrent.go b/src/server/torr/torrent.go index 7a80be1..a787442 100644 --- a/src/server/torr/torrent.go +++ b/src/server/torr/torrent.go @@ -24,7 +24,8 @@ type Torrent struct { Poster string *torrent.TorrentSpec - Status state.TorrentStatus + Stat state.TorrentStat + Timestamp int64 ///// *torrent.Torrent @@ -50,7 +51,6 @@ type Torrent struct { } func NewTorrent(spec *torrent.TorrentSpec, bt *BTServer) (*Torrent, error) { - switch settings.BTsets.RetrackersMode { case 1: 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.Torrent = goTorrent - torr.Status = state.TorrentAdded + torr.Stat = state.TorrentAdded torr.lastTimeSpeed = time.Now() torr.bt = bt torr.closed = goTorrent.Closed() torr.TorrentSpec = spec torr.expiredTime = time.Now().Add(time.Minute) + torr.Timestamp = time.Now().Unix() go torr.watch() @@ -106,12 +107,12 @@ func (t *Torrent) WaitInfo() bool { } func (t *Torrent) GotInfo() bool { - if t.Status == state.TorrentClosed { + if t.Stat == state.TorrentClosed { return false } - t.Status = state.TorrentGettingInfo + t.Stat = state.TorrentGettingInfo if t.WaitInfo() { - t.Status = state.TorrentWorking + t.Stat = state.TorrentWorking t.expiredTime = time.Now().Add(time.Minute * 5) return true } else { @@ -179,7 +180,7 @@ func (t *Torrent) updateRA() { } 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 { @@ -208,7 +209,7 @@ func (t *Torrent) Length() int64 { } func (t *Torrent) NewReader(file *torrent.File, readahead int64) *Reader { - if t.Status == state.TorrentClosed { + if t.Stat == state.TorrentClosed { return nil } reader := NewReader(t, file, readahead) @@ -230,14 +231,14 @@ func (t *Torrent) Preload(index int, size int64) { return } - if t.Status == state.TorrentGettingInfo { + if t.Stat == state.TorrentGettingInfo { t.WaitInfo() // wait change status time.Sleep(100 * time.Millisecond) } t.muTorrent.Lock() - if t.Status != state.TorrentWorking { + if t.Stat != state.TorrentWorking { t.muTorrent.Unlock() return } @@ -249,12 +250,12 @@ func (t *Torrent) Preload(index int, size int64) { t.muTorrent.Unlock() return } - t.Status = state.TorrentPreload + t.Stat = state.TorrentPreload t.muTorrent.Unlock() defer func() { - if t.Status == state.TorrentPreload { - t.Status = state.TorrentWorking + if t.Stat == state.TorrentPreload { + t.Stat = state.TorrentWorking } }() @@ -299,7 +300,7 @@ func (t *Torrent) Preload(index int, size int64) { t.PreloadSize = size var lastSize int64 = 0 errCount := 0 - for t.Status == state.TorrentPreload { + for t.Stat == state.TorrentPreload { t.expiredTime = time.Now().Add(time.Minute * 5) 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) @@ -330,7 +331,7 @@ func (t *Torrent) drop() { } func (t *Torrent) Close() { - t.Status = state.TorrentClosed + t.Stat = state.TorrentClosed t.bt.mu.Lock() defer t.bt.mu.Unlock() @@ -341,16 +342,17 @@ func (t *Torrent) Close() { t.drop() } -func (t *Torrent) Stats() *state.TorrentStats { +func (t *Torrent) Status() *state.TorrentStatus { t.muTorrent.Lock() defer t.muTorrent.Unlock() - st := new(state.TorrentStats) + st := new(state.TorrentStatus) - st.TorrentStatus = t.Status - st.TorrentStatusString = t.Status.String() + st.Stat = t.Stat + st.StatString = t.Stat.String() st.Title = t.Title st.Poster = t.Poster + st.Timestamp = t.Timestamp if t.TorrentSpec != nil { st.Hash = t.TorrentSpec.InfoHash.HexString() diff --git a/src/server/utils/filetypes.go b/src/server/utils/filetypes.go index 268ca45..f7e41e8 100644 --- a/src/server/utils/filetypes.go +++ b/src/server/utils/filetypes.go @@ -78,7 +78,7 @@ func GetMimeType(filename string) string { return "*/*" } -func GetPlayableFiles(st state.TorrentStats) []state.TorrentFileStat { +func GetPlayableFiles(st state.TorrentStatus) []state.TorrentFileStat { files := make([]state.TorrentFileStat, 0) for _, f := range st.FileStats { if GetMimeType(f.Path) != "*/*" { diff --git a/src/server/web/api/m3u.go b/src/server/web/api/m3u.go index 5e2d8d9..5fe3b6d 100644 --- a/src/server/web/api/m3u.go +++ b/src/server/web/api/m3u.go @@ -8,7 +8,9 @@ import ( "path/filepath" "time" + "github.com/anacrolix/missinggo/httptoo" sets "server/settings" + "server/torr" "server/torr/state" "server/utils" @@ -18,21 +20,17 @@ import ( func allPlayList(c *gin.Context) { _, fromlast := c.GetQuery("fromlast") - stats := listTorrents() + torrs := torr.ListTorrent() host := "http://" + c.Request.Host list := "#EXTM3U\n" - - for _, stat := range stats { - list += getM3uList(stat, host, fromlast) + hash := "" + for _, tr := range torrs { + list += getM3uList(tr.Status(), host, fromlast) + hash += tr.Hash().HexString() } - c.Header("Content-Type", "audio/x-mpegurl") - c.Header("Connection", "close") - c.Header("Content-Disposition", `attachment; filename="all.m3u"`) - http.ServeContent(c.Writer, c.Request, "all.m3u", time.Now(), bytes.NewReader([]byte(list))) - - c.Status(200) + sendM3U(c, "all.m3u", hash, list) } func playList(c *gin.Context) { @@ -43,34 +41,43 @@ func playList(c *gin.Context) { return } - stats := listTorrents() - var stat *state.TorrentStats - for _, st := range stats { - if st.Hash == hash { - stat = st + tors := torr.ListTorrent() + var tor *torr.Torrent + for _, tr := range tors { + if tr.Hash().HexString() == hash { + tor = tr break } } - if stat == nil { + if tor == nil { c.AbortWithStatus(http.StatusNotFound) return } // TODO проверить host := "http://" + c.Request.Host - list := getM3uList(stat, host, fromlast) + list := getM3uList(tor.Status(), host, fromlast) 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("Connection", "close") - c.Header("Content-Disposition", `attachment; filename="playlist.m3u"`) - http.ServeContent(c.Writer, c.Request, "playlist.m3u", time.Now(), bytes.NewReader([]byte(list))) - + if hash != "" { + 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) } -func getM3uList(tor *state.TorrentStats, host string, fromLast bool) string { +func getM3uList(tor *state.TorrentStatus, host string, fromLast bool) string { m3u := "" from := 0 if fromLast { @@ -87,15 +94,20 @@ func getM3uList(tor *state.TorrentStats, host string, fromLast bool) string { fn = f.Path } m3u += "#EXTINF:0," + fn + "\n" - // http://127.0.0.1:8090/stream/fname?link=...&index=0&play - m3u += host + "/stream/" + url.QueryEscape(f.Path) + "?link=" + tor.Hash + "&file=" + fmt.Sprint(f.Id) + "\n" + title := filepath.Base(f.Path) + 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 } -func searchLastPlayed(tor *state.TorrentStats) int { +func searchLastPlayed(tor *state.TorrentStatus) int { //TODO проверить viewed := sets.ListViewed(tor.Hash) for i := len(tor.FileStats); i > 0; i-- { diff --git a/src/server/web/api/route.go b/src/server/web/api/route.go index f4c16af..b8e382d 100644 --- a/src/server/web/api/route.go +++ b/src/server/web/api/route.go @@ -22,6 +22,7 @@ func SetupRouteApi(route *gin.Engine, serv *torr.BTServer) { route.POST("/settings", settings) route.POST("/torrents", torrents) + route.POST("/torrent/upload", torrentUpload) route.GET("/stream", stream) route.GET("/stream/*fname", stream) diff --git a/src/server/web/api/stream.go b/src/server/web/api/stream.go index c5b9b5c..e8e51e8 100644 --- a/src/server/web/api/stream.go +++ b/src/server/web/api/stream.go @@ -4,6 +4,7 @@ import ( "net/http" "net/url" "strconv" + "strings" "server/torr" "server/web/api/utils" @@ -12,12 +13,17 @@ import ( "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&fromlast +// stream torrent // 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&title=...&poster=... +// only save +// http://127.0.0.1:8090/stream/fname?link=...&save&title=...&poster=... func stream(c *gin.Context) { link := c.Query("link") @@ -26,6 +32,7 @@ func stream(c *gin.Context) { _, stat := c.GetQuery("stat") _, save := c.GetQuery("save") _, m3u := c.GetQuery("m3u") + _, fromlast := c.GetQuery("fromlast") _, play := c.GetQuery("play") title := c.Query("title") poster := c.Query("poster") @@ -37,10 +44,13 @@ func stream(c *gin.Context) { if title == "" { title = c.Param("fname") + title, _ = url.PathUnescape(title) + title = strings.TrimLeft(title, "/") + } else { + title, _ = url.QueryUnescape(title) } link, _ = url.QueryUnescape(link) - title, _ = url.QueryUnescape(title) poster, _ = url.QueryUnescape(poster) spec, err := utils.ParseLink(link) @@ -49,43 +59,16 @@ func stream(c *gin.Context) { return } - var tor *torr.Torrent - - // find torrent in bts - for _, torrent := range bts.ListTorrents() { - if torrent.Hash().HexString() == spec.InfoHash.HexString() { - tor = torrent - } + tor, err := torr.AddTorrent(spec, title, poster) + if err != nil { + c.AbortWithError(http.StatusInternalServerError, err) + return } - // 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 if save { - utils.AddTorrent(tor) - c.Status(200) - } - // wait torrent info - if !tor.GotInfo() { - c.AbortWithError(http.StatusInternalServerError, errors.New("timeout torrent get info")) - return + torr.SaveTorrentToDB(tor) + c.Status(200) // only set status, not return } // find file @@ -98,7 +81,7 @@ func stream(c *gin.Context) { 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")) return } @@ -107,14 +90,14 @@ func stream(c *gin.Context) { tor.Preload(index, 0) } // return stat if query - if stat || (!m3u && !play) { - c.JSON(200, tor.Stats()) + if stat { + c.JSON(200, tor.Status()) return } else // return m3u if query if m3u { - //TODO m3u - c.JSON(200, tor.Stats()) + m3ulist := "#EXTM3U\n" + getM3uList(tor.Status(), "http://"+c.Request.Host, fromlast) + sendM3U(c, tor.Name(), tor.Hash().HexString(), m3ulist) return } else // return play if query @@ -123,96 +106,3 @@ func stream(c *gin.Context) { 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 index c7b26ff..4e3ac11 100644 --- a/src/server/web/api/torrents.go +++ b/src/server/web/api/torrents.go @@ -8,7 +8,6 @@ import ( "server/torr/state" "server/web/api/utils" - "github.com/anacrolix/torrent/metainfo" "github.com/gin-gonic/gin" "github.com/pkg/errors" ) @@ -56,6 +55,11 @@ func torrents(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) torrSpec, err := utils.ParseLink(req.Link) if err != nil { @@ -64,44 +68,30 @@ func addTorrent(req torrReqJS, c *gin.Context) { return } - torr, err := torr.NewTorrent(torrSpec, bts) + tor, err := torr.AddTorrent(torrSpec, req.Title, req.Poster) if err != nil { log.TLogln("error add torrent:", err) c.AbortWithError(http.StatusInternalServerError, err) 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 { - log.TLogln("save to db:", torr.Torrent.InfoHash().HexString()) - utils.AddTorrent(torr) + torr.SaveTorrentToDB(tor) } - st := torr.Stats() + st := tor.Status() c.JSON(200, st) } func getTorrent(req torrReqJS, c *gin.Context) { - hash := metainfo.NewHashFromHex(req.Hash) - tor := bts.GetTorrent(hash) - if tor == nil { - tor = utils.GetTorrent(hash) + if req.Hash == "" { + c.AbortWithError(http.StatusBadRequest, errors.New("hash is empty")) + return } + tor := torr.GetTorrent(req.Hash) if tor != nil { - st := tor.Stats() + st := tor.Status() c.JSON(200, st) } else { c.Status(http.StatusNotFound) @@ -109,44 +99,28 @@ func getTorrent(req torrReqJS, c *gin.Context) { } func remTorrent(req torrReqJS, c *gin.Context) { - hash := metainfo.NewHashFromHex(req.Hash) - bts.RemoveTorrent(hash) - utils.RemTorrent(hash) + if req.Hash == "" { + c.AbortWithError(http.StatusBadRequest, errors.New("hash is empty")) + return + } + torr.RemTorrent(req.Hash) c.Status(200) } 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) } -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) { if req.Hash == "" { c.AbortWithError(http.StatusBadRequest, errors.New("hash is empty")) return } - hash := metainfo.NewHashFromHex(req.Hash) - - bts.RemoveTorrent(hash) + torr.DropTorrent(req.Hash) c.Status(200) } diff --git a/src/server/web/api/upload.go b/src/server/web/api/upload.go new file mode 100644 index 0000000..307df86 --- /dev/null +++ b/src/server/web/api/upload.go @@ -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) +} diff --git a/src/server/web/api/utils/link.go b/src/server/web/api/utils/link.go index bbe59f4..50801dd 100644 --- a/src/server/web/api/utils/link.go +++ b/src/server/web/api/utils/link.go @@ -3,6 +3,7 @@ package utils import ( "errors" "fmt" + "mime/multipart" "net/http" "net/url" "runtime" @@ -13,6 +14,25 @@ import ( "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) { urlLink, err := url.Parse(link) if err != nil { @@ -40,9 +60,14 @@ func fromMagnet(link string) (*torrent.TorrentSpec, error) { return nil, err } + var trackers [][]string + if len(mag.Trackers) > 0 { + trackers = [][]string{mag.Trackers} + } + return &torrent.TorrentSpec{ InfoBytes: nil, - Trackers: [][]string{mag.Trackers}, + Trackers: trackers, DisplayName: mag.DisplayName, InfoHash: mag.InfoHash, }, nil diff --git a/src/server/web/api/viewed.go b/src/server/web/api/viewed.go index c882eb0..8494b26 100644 --- a/src/server/web/api/viewed.go +++ b/src/server/web/api/viewed.go @@ -7,6 +7,10 @@ import ( sets "server/settings" ) +/* +file index starts from 1 +*/ + // Action: set, rem, list type viewedReqJS struct { requestI