diff --git a/server/web/msx/msx.go b/server/web/msx/msx.go index 9b99511..3422e2d 100644 --- a/server/web/msx/msx.go +++ b/server/web/msx/msx.go @@ -1,141 +1,162 @@ package msx import ( - _ "embed" "encoding/json" "net/http" - "sync" - + "net/url" + "os" + "path" + "path/filepath" + "server/settings" + "server/torr" "server/version" "server/web/auth" + "strconv" + "strings" "github.com/gin-gonic/gin" ) -var ( - //go:embed russian.json.gz - rus []byte - //go:embed torrents.js.gz - trs []byte - //go:embed torrent.js.gz - trn []byte - //go:embed ts.js.gz - its []byte - - idb = new(sync.Mutex) - ids = make(map[string]string) -) - -func asset(c *gin.Context, t string, d []byte) { - c.Header("Content-Encoding", "gzip") - c.Data(200, t+"; charset=UTF-8", d) -} +const base, fls = "https://damiva.github.io/msx", "files" func SetupRoute(r gin.IRouter) { authorized := r.Group("/", auth.CheckAuth()) - - authorized.GET("/msx/:pth", msxPTH) - authorized.GET("/msx/imdb", msxIMDB) - authorized.GET("/msx/imdb/:id", msxIMDBID) -} - -// msxPTH godoc -// -// @Summary Multi usage endpoint -// @Description Multi usage endpoint. -// -// @Tags MSX -// -// @Param link path string true "Route MSX pages" -// -// @Produce json -// @Success 200 "Data returned according to path" -// @Router /msx/{pth} [get] -func msxPTH(c *gin.Context) { - js := []string{"http://msx.benzac.de/js/tvx-plugin.min.js"} - switch p := c.Param("pth"); p { - case "start.json": - c.JSON(200, map[string]string{ - "name": "TorrServer", - "version": version.Version, - "parameter": "menu:request:interaction:init@{PREFIX}{SERVER}/msx/ts", - }) - case "russian.json": - asset(c, "application/json", rus) - case "torrents.js": - asset(c, "text/javascript", trs) - case "torrent.js": - asset(c, "text/javascript", trn) - case "ts.js": - asset(c, "text/javascript", its) - case "torrents": - js = append(js, p+".js") - p = "torrent" - fallthrough - case "torrent": - if c.Query("platform") == "tizen" { - js = append(js, "http://msx.benzac.de/interaction/js/tizen-player.js") - } - fallthrough - case "ts": - b := []byte("\n\n\nTorrServer Plugin\n\n") - for _, j := range append(js, p+".js") { - b = append(b, "\n"...) - } - c.Data(200, "text/html; charset=UTF-8", append(b, "\n\n"...)) - default: - c.AbortWithStatus(404) - } -} - -// msxIMDB godoc -// -// @Summary Get MSX IMDB informations -// @Description Get MSX IMDB informations. -// -// @Tags MSX -// -// @Produce json -// @Success 200 "JSON MSX IMDB informations" -// @Router /msx/imdb [get] -func msxIMDB(c *gin.Context) { - idb.Lock() - defer idb.Unlock() - l := len(ids) - ids = make(map[string]string) - c.JSON(200, l) -} - -// msxIMDB godoc -// -// @Summary Get MSX IMDB informations -// @Description Get MSX IMDB informations. -// -// @Tags MSX -// -// @Param id path string true "IMDB ID" -// -// @Produce json -// @Success 200 "JSON MSX IMDB informations" -// @Router /msx/imdb/{id} [get] -func msxIMDBID(c *gin.Context) { - idb.Lock() - defer idb.Unlock() - p := c.Param("id") - i, o := ids[p] - if !o { - if r, e := http.Get("https://v2.sg.media-imdb.com/suggestion/h/" + p + ".json"); e == nil { - defer r.Body.Close() - if r.StatusCode == 200 { - var j struct { - D []struct{ I struct{ ImageUrl string } } - } - if e = json.NewDecoder(r.Body).Decode(&j); e == nil && len(j.D) > 0 { - i = j.D[0].I.ImageUrl + authorized.Any("/msx", func(c *gin.Context) { + if l := c.Query("url"); l != "" { + proxy(c, l, c.QueryArray("header")...) + } else if l = c.Query("indb"); l != "" { + var r bool + for _, t := range settings.ListTorrent() { + if r = t.InfoHash.HexString() == l; r { + break } } + c.JSON(200, r) + } else if c.Request.Method == "POST" { + serve(c) + } else { + proxy(c, base+"/ts.html") } - ids[p] = i - } - c.JSON(200, i) + }) + authorized.GET("/msx/*pth", func(c *gin.Context) { + p := c.Param("pth") + if _, n := path.Split(p); n == "" { + files(c, filepath.Join(fls, filepath.Clean(p))) + } else if n = strings.ToLower(path.Ext(n)); n == "" { + c.AbortWithStatus(http.StatusNotFound) + } else if n == ".html" || n == ".js" || n == ".json" { + proxy(c, base+p) + } else { + c.File(filepath.Join(fls, filepath.Clean(p))) + } + }) + authorized.GET("/imdb/:id", func(c *gin.Context) { + const x = ".json" + i, l := c.Param("id"), "" + j := strings.HasSuffix(i, x) + if i = strings.TrimSuffix(i, x); i != "" { + if r, e := http.Get("https://v2.sg.media-imdb.com/suggestion/h/" + i + x); e == nil { + if r.StatusCode == http.StatusOK { + var j struct { + D []struct{ I struct{ ImageUrl string } } + } + if e = json.NewDecoder(r.Body).Decode(&j); e == nil && len(j.D) > 0 { + l = j.D[0].I.ImageUrl + } + } + r.Body.Close() + } + } + if j { + c.JSON(200, l) + } else if l == "" { + c.Status(http.StatusNotFound) + } else { + c.Redirect(http.StatusMovedPermanently, l) + } + }) +} +func proxy(c *gin.Context, u string, h ...string) { + if u == "" { + c.AbortWithStatus(http.StatusBadRequest) + } else if q, e := http.NewRequest(c.Request.Method, u, c.Request.Body); e != nil { + c.AbortWithError(http.StatusInternalServerError, e) + } else { + for _, v := range h { + if v := strings.SplitN(v, ":", 2); len(v) == 2 { + q.Header.Add(v[0], v[1]) + } + } + if r, e := http.DefaultClient.Do(q); e != nil { + c.AbortWithError(http.StatusInternalServerError, e) + } else { + c.DataFromReader(r.StatusCode, r.ContentLength, r.Header.Get("Content-Type"), r.Body, nil) + r.Body.Close() + } + } +} +func serve(c *gin.Context) { + var j struct { + Data struct { + Update string + Info struct{ Content struct{ Flag string } } + } + } + if e := c.Bind(&j); e != nil { + c.AbortWithError(http.StatusBadRequest, e) + } else if j.Data.Update == "" && j.Data.Info.Content.Flag == "" { + r := map[string]any{"version": version.Version, "search": settings.BTsets.EnableRutorSearch} + if l, e := os.Readlink(fls); e == nil { + r["files"] = l + } else if !os.IsNotExist(e) { + r["error"] = e.Error() + } + c.JSON(200, r) + } else { + var r map[string]any + h, sc, st := j.Data.Info.Content.Flag, "", "" + if h == "" { + h = j.Data.Update[strings.LastIndexByte(j.Data.Update, ':')+1:] + } + if t := torr.GetTorrent(h); t != nil { + if t := t.Status(); t != nil && t.Stat < 5 { + switch t.Stat { + case 4: + sc = "msx-red" + case 3: + sc = "msx-green" + default: + sc = "msx-yellow" + } + st = "{ico:north} " + strconv.Itoa(t.ActivePeers) + " / " + strconv.Itoa(t.TotalPeers) + " {ico:south} " + strconv.Itoa(t.ConnectedSeeders) + } + } + if j.Data.Update != "" { + r = map[string]any{"action": "update:" + j.Data.Update, "data": map[string]string{"stamp": st, "stampColor": sc}} + } else { + if sc != "" { + sc = "{tb}{tb}{col:" + sc + "}" + } + r = map[string]any{"action": "player:label:position:{LABEL}" + sc + st} + } + c.JSON(200, map[string]any{"response": map[string]any{"status": http.StatusOK, "data": r}}) + } +} +func files(c *gin.Context, p string) { + if d, e := os.ReadDir(p); e == nil { + var ds, fs []map[string]any + u := c.Request.URL.EscapedPath() + for _, f := range d { + if n := f.Name(); f.IsDir() { + ds = append(ds, map[string]any{"id": u + url.PathEscape(n) + "/", "path": n}) + } else if f, e := f.Info(); e == nil { + fs = append(fs, map[string]any{"id": u + url.PathEscape(n), "path": n, "length": f.Size()}) + } + } + c.JSON(200, map[string]any{"title": filepath.Base(strings.TrimSuffix(p, "/")), "path": u, "files": append(ds, fs...)}) + } else if os.IsNotExist(e) { + c.AbortWithError(http.StatusNotFound, e) + } else { + c.AbortWithError(http.StatusInternalServerError, e) + } } diff --git a/server/web/msx/russian.json.gz b/server/web/msx/russian.json.gz deleted file mode 100644 index fc4f98f..0000000 Binary files a/server/web/msx/russian.json.gz and /dev/null differ diff --git a/server/web/msx/torrent.js.gz b/server/web/msx/torrent.js.gz deleted file mode 100644 index bc246dc..0000000 Binary files a/server/web/msx/torrent.js.gz and /dev/null differ diff --git a/server/web/msx/torrents.js.gz b/server/web/msx/torrents.js.gz deleted file mode 100644 index 8d479bd..0000000 Binary files a/server/web/msx/torrents.js.gz and /dev/null differ diff --git a/server/web/msx/ts.js.gz b/server/web/msx/ts.js.gz deleted file mode 100644 index 4f6236c..0000000 Binary files a/server/web/msx/ts.js.gz and /dev/null differ