This commit is contained in:
YouROK
2020-11-10 14:06:13 +03:00
parent c95eaccc57
commit 6a11651cf5
15 changed files with 566 additions and 209 deletions

109
src/server/web/api/m3u.go Normal file
View File

@@ -0,0 +1,109 @@
package api
import (
"bytes"
"fmt"
"net/http"
"net/url"
"path/filepath"
"time"
"github.com/gin-gonic/gin"
"github.com/pkg/errors"
sets "server/settings"
"server/torr/state"
"server/utils"
)
func allPlayList(c *gin.Context) {
_, fromlast := c.GetQuery("fromlast")
stats := listTorrents()
host := "http://" + c.Request.Host
list := "#EXTM3U\n"
for _, stat := range stats {
list += getM3uList(stat, host, fromlast)
}
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)
}
func playList(c *gin.Context) {
hash := c.Query("torrhash")
_, fromlast := c.GetQuery("fromlast")
if hash == "" {
c.AbortWithError(http.StatusBadRequest, errors.New("hash is empty"))
return
}
stats := listTorrents()
var stat *state.TorrentStats
for _, st := range stats {
if st.Hash == hash {
stat = st
break
}
}
if stat == nil {
c.AbortWithStatus(http.StatusNotFound)
return
}
// TODO проверить
host := "http://" + c.Request.Host
list := getM3uList(stat, host, fromlast)
list = "#EXTM3U\n" + list
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)))
c.Status(200)
}
func getM3uList(tor *state.TorrentStats, host string, fromLast bool) string {
m3u := ""
from := 0
if fromLast {
pos := searchLastPlayed(tor)
if pos != -1 {
from = pos
}
}
for i, f := range tor.FileStats {
if i >= from {
if utils.GetMimeType(f.Path) != "*/*" {
fn := filepath.Base(f.Path)
if fn == "" {
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"
}
}
}
return m3u
}
func searchLastPlayed(tor *state.TorrentStats) int {
//TODO проверить
viewed := sets.ListViewed(tor.Hash)
for i := len(tor.FileStats); i > 0; i-- {
stat := tor.FileStats[i]
for _, v := range viewed {
if stat.Id == v.FileIndex {
return v.FileIndex
}
}
}
return -1
}

View File

@@ -25,6 +25,11 @@ func SetupRouteApi(route *gin.Engine, serv *torr.BTServer) {
route.GET("/stream", stream)
route.GET("/stream/*fname", stream)
route.POST("/viewed", viewed)
route.GET("/playlist/all.m3u", allPlayList)
route.GET("/playlist", playList)
}
func echo(c *gin.Context) {

View File

@@ -13,10 +13,6 @@ 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
@@ -27,9 +23,7 @@ func settings(c *gin.Context) {
}
if req.Action == "get" {
resp := setsRespJS{}
resp.Sets = settings2.BTsets
c.JSON(200, resp)
c.JSON(200, settings2.BTsets)
return
}
if req.Action == "set" {

View File

@@ -2,6 +2,7 @@ package api
import (
"net/http"
"net/url"
"strconv"
"github.com/gin-gonic/gin"
@@ -10,6 +11,13 @@ import (
"server/web/api/utils"
)
// http://127.0.0.1:8090/stream/fname?link=...&index=0&stat
// http://127.0.0.1:8090/stream/fname?link=...&index=0&m3u
// http://127.0.0.1:8090/stream/fname?link=...&index=0&play
// http://127.0.0.1:8090/stream/fname?link=...&save&title=...&poster=...
// http://127.0.0.1:8090/stream/fname?link=...&index=0&play&save
// http://127.0.0.1:8090/stream/fname?link=...&index=0&play&save&title=...&poster=...
func stream(c *gin.Context) {
link := c.Query("link")
indexStr := c.Query("index")
@@ -21,13 +29,19 @@ func stream(c *gin.Context) {
title := c.Query("title")
poster := c.Query("poster")
// TODO unescape args
if link == "" {
c.AbortWithError(http.StatusBadRequest, errors.New("link should not be empty"))
return
}
if title == "" {
title = c.Param("fname")
}
link, _ = url.QueryUnescape(link)
title, _ = url.QueryUnescape(title)
poster, _ = url.QueryUnescape(poster)
spec, err := utils.ParseLink(link)
if err != nil {
c.AbortWithError(http.StatusInternalServerError, err)
@@ -65,6 +79,7 @@ func stream(c *gin.Context) {
// save to db
if save {
utils.AddTorrent(tor)
c.Status(200)
}
// wait torrent info
if !tor.WaitInfo() {
@@ -94,13 +109,13 @@ func stream(c *gin.Context) {
if stat || (!m3u && !play) {
c.JSON(200, tor.Stats())
return
}
} else
// return m3u if query
if m3u {
//TODO m3u
c.JSON(200, tor.Stats())
return
}
} else
// return play if query
if play {
tor.Stream(index, c.Request, c.Writer)

View File

@@ -5,11 +5,14 @@ import (
"github.com/anacrolix/torrent/metainfo"
"github.com/gin-gonic/gin"
"github.com/pkg/errors"
"server/log"
"server/torr"
"server/torr/state"
"server/web/api/utils"
)
//Action: add, get, rem, list
//Action: add, get, rem, list, drop
type torrReqJS struct {
requestI
Link string `json:"link,omitempty"`
@@ -26,43 +29,62 @@ func torrents(c *gin.Context) {
c.AbortWithError(http.StatusBadRequest, err)
return
}
c.Status(http.StatusBadRequest)
switch req.Action {
case "add":
{
add(req, c)
addTorrent(req, c)
}
case "get":
{
get(req, c)
getTorrent(req, c)
}
case "rem":
{
rem(req, c)
remTorrent(req, c)
}
case "list":
{
list(req, c)
listTorrent(req, c)
}
case "drop":
{
dropTorrent(req, c)
}
}
}
func add(req torrReqJS, c *gin.Context) {
func addTorrent(req torrReqJS, c *gin.Context) {
log.TLogln("add torrent", req.Link)
torrSpec, err := utils.ParseLink(req.Link)
if err != nil {
log.TLogln("error add torrent:", err)
c.AbortWithError(http.StatusBadRequest, err)
return
}
torr, err := torr.NewTorrent(torrSpec, bts)
if err != nil {
log.TLogln("error add torrent:", err)
c.AbortWithError(http.StatusInternalServerError, err)
return
}
if !torr.WaitInfo() {
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)
}
@@ -70,7 +92,7 @@ func add(req torrReqJS, c *gin.Context) {
c.JSON(200, st)
}
func get(req torrReqJS, c *gin.Context) {
func getTorrent(req torrReqJS, c *gin.Context) {
hash := metainfo.NewHashFromHex(req.Hash)
tor := bts.GetTorrent(hash)
if tor == nil {
@@ -85,17 +107,22 @@ func get(req torrReqJS, c *gin.Context) {
}
}
func rem(req torrReqJS, c *gin.Context) {
func remTorrent(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) {
func listTorrent(req torrReqJS, c *gin.Context) {
stats := listTorrents()
c.JSON(200, stats)
}
func listTorrents() []*state.TorrentStats {
btlist := bts.ListTorrents()
dblist := utils.ListTorrents()
var stats []torr.TorrentStats
var stats []*state.TorrentStats
for _, tr := range btlist {
stats = append(stats, tr.Stats())
}
@@ -109,6 +136,16 @@ mainloop:
}
stats = append(stats, db.Stats())
}
c.JSON(200, 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)
c.Status(200)
}

View File

@@ -0,0 +1,53 @@
package api
import (
"net/http"
"github.com/gin-gonic/gin"
sets "server/settings"
)
// Action: set, rem, list
type viewedReqJS struct {
requestI
*sets.Viewed
}
func viewed(c *gin.Context) {
var req viewedReqJS
err := c.ShouldBindJSON(&req)
if err != nil {
c.AbortWithError(http.StatusBadRequest, err)
return
}
switch req.Action {
case "set":
{
setViewed(req, c)
}
case "rem":
{
remViewed(req, c)
}
case "list":
{
listViewed(req, c)
}
}
}
func setViewed(req viewedReqJS, c *gin.Context) {
sets.SetViewed(req.Viewed)
c.Status(200)
}
func remViewed(req viewedReqJS, c *gin.Context) {
sets.RemViewed(req.Viewed)
c.Status(200)
}
func listViewed(req viewedReqJS, c *gin.Context) {
list := sets.ListViewed(req.Hash)
c.JSON(200, list)
}