diff --git a/server/web/api/httpfs/httpfs.go b/server/web/api/httpfs/httpfs.go
new file mode 100644
index 00000000..3aef1324
--- /dev/null
+++ b/server/web/api/httpfs/httpfs.go
@@ -0,0 +1,112 @@
+package httpfs
+
+import (
+ "strings"
+ "net/http"
+ "net/url"
+ "strconv"
+ "server/log"
+ "github.com/gin-gonic/gin"
+ "server/torr"
+ "github.com/pkg/errors"
+ "github.com/anacrolix/torrent"
+
+ "server/torr/state"
+
+ sf "github.com/sa-/slicefunk"
+)
+
+func listDir(path string, folders [] string, files []string) string {
+ result := `
+
+
+
+ Directory listing for ` + path + `
+ Directory listing for ` + path + `
`
+
+ for _, str := range folders {
+ result += `- ` + str + `/
`
+ }
+ for _, str := range files {
+ result += `- ` + str + `
`
+ }
+ result += `
`
+ return result
+}
+
+
+
+func HandleHttpfs(c *gin.Context) {
+ path := c.Param("path")
+ log.TLogln("URL path", path)
+ path = strings.Trim(path, "/")
+
+ if path == "" {
+ newArray := sf.Map(torr.ListTorrent(), func(item * torr.Torrent) string { return item.Title })
+ c.Header("Content-Type", "text/html; charset=utf-8")
+ c.String(200, listDir(path, newArray, []string{}))
+ } else {
+ folders := strings.Split(path, "/")
+ trName := folders[0]
+ folderPath := strings.Join(folders[1:], "/")
+// for _, item := range torr.ListTorrent() {
+// log.TLogln("Torrents found", item.Title)
+// }
+ requestedTorrents := sf.Filter(torr.ListTorrent(), func(item * torr.Torrent) bool { return item.Title == trName })
+ if len(requestedTorrents) != 1 {
+ c.AbortWithError(http.StatusBadRequest, errors.New("Torrent not found" + trName + ": " + strconv.Itoa(len(requestedTorrents))))
+ return
+ }
+ requestedTorrent := torr.GetTorrent(requestedTorrents[0].Hash().HexString())
+ if requestedTorrent == nil {
+ c.AbortWithStatus(http.StatusNotFound)
+ return
+ }
+
+ if requestedTorrent.Stat == state.TorrentInDB {
+ requestedTorrent = torr.LoadTorrent(requestedTorrent)
+ if requestedTorrent == nil {
+ c.AbortWithError(http.StatusInternalServerError, errors.New("error get torrent info"))
+ return
+ }
+ }
+
+ newArray := sf.Filter(requestedTorrent.Files(), func(item * torrent.File) bool { return strings.HasPrefix(item.Path(), folderPath) })
+
+// for _, item := range newArray {
+// log.TLogln("Filtered files", item.Path(), " sha1: ")// + item.FileInfo())
+// }
+//
+ if len(newArray) == 1 && newArray[0].Path() == folderPath {
+ log.TLogln("Downloading file:", requestedTorrent.Title, ":", newArray[0].Path())
+ var index = -1
+ for i, item := range requestedTorrent.Files() {
+ if item.Path() == folderPath {
+ index = i + 1
+ break
+ }
+ }
+ names := strings.Split(newArray[0].Path(), "/")
+// torr.Preload(requestedTorrent, index)
+ c.Header("Content-Disposition", `attachment; filename="`+names[len(names) - 1]+`"`)
+ c.Header("Content-Type", "application/octet-stream")
+ requestedTorrent.Stream(index, c.Request, c.Writer)
+ } else {
+ c.Header("Content-Type", "text/html; charset=utf-8")
+ folders := []string{}
+ files := []string{}
+
+ for _, item := range newArray {
+ p := strings.TrimPrefix(item.Path(), folderPath + "/")
+ if strings.Contains(p, "/") {
+ folders = append(folders, strings.Split(p, "/")[0])
+ } else {
+ files = append(files, p)
+ }
+ }
+ folders = sf.Unique(folders)
+ c.String(200, listDir(path, folders, files))
+ }
+ }
+
+}
diff --git a/server/web/api/route.go b/server/web/api/route.go
index 24620b7c..557d4efb 100644
--- a/server/web/api/route.go
+++ b/server/web/api/route.go
@@ -3,6 +3,8 @@ package api
import (
config "server/settings"
"server/web/auth"
+ "net/http"
+ "server/web/api/httpfs"
"github.com/gin-gonic/gin"
)
@@ -50,4 +52,7 @@ func SetupRoute(route gin.IRouter) {
}
authorized.GET("/ffp/:hash/:id", ffp)
+
+ authorized.GET("/httpfs", func(c *gin.Context) { c.Redirect(http.StatusFound, "/httpfs/") })
+ authorized.GET("/httpfs/*path", httpfs.HandleHttpfs)
}
diff --git a/web/src/components/App/Sidebar.jsx b/web/src/components/App/Sidebar.jsx
index 4654c706..6ff081ae 100644
--- a/web/src/components/App/Sidebar.jsx
+++ b/web/src/components/App/Sidebar.jsx
@@ -2,7 +2,7 @@ import Divider from '@material-ui/core/Divider'
import ListItem from '@material-ui/core/ListItem'
import ListItemIcon from '@material-ui/core/ListItemIcon'
import ListItemText from '@material-ui/core/ListItemText'
-import { CreditCard as CreditCardIcon } from '@material-ui/icons'
+import { CreditCard as CreditCardIcon, Http as HttpIcon } from '@material-ui/icons'
import List from '@material-ui/core/List'
import { useTranslation } from 'react-i18next'
import AddDialogButton from 'components/Add'
@@ -60,6 +60,14 @@ const Sidebar = ({ isDrawerOpen, setIsDonationDialogOpen, isOffline, isLoading,
+ window.open('httpfs', '_blank')}>
+
+
+
+
+
+
+