diff --git a/go.mod b/go.mod index da830e3..b0547aa 100644 --- a/go.mod +++ b/go.mod @@ -3,13 +3,13 @@ module TorPlayer2 go 1.21 require ( + fyne.io/systray v1.10.0 github.com/a-h/templ v0.2.513 github.com/adrg/xdg v0.4.0 github.com/anacrolix/torrent v1.53.3 github.com/asticode/go-astisub v0.26.2 github.com/gabriel-vasile/mimetype v1.4.3 github.com/gen2brain/beeep v0.0.0-20240112042604-c7bb2cd88fea - github.com/getlantern/systray v1.2.2 github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c gopkg.in/yaml.v3 v3.0.1 ) @@ -17,18 +17,11 @@ require ( require ( github.com/asticode/go-astikit v0.20.0 // indirect github.com/asticode/go-astits v1.8.0 // indirect - github.com/getlantern/context v0.0.0-20190109183933-c447772a6520 // indirect - github.com/getlantern/errors v0.0.0-20190325191628-abdb3e3e36f7 // indirect - github.com/getlantern/golog v0.0.0-20190830074920-4ef2e798c2d7 // indirect - github.com/getlantern/hex v0.0.0-20190417191902-c6586a6fe0b7 // indirect - github.com/getlantern/hidden v0.0.0-20190325191715-f02dbb02be55 // indirect - github.com/getlantern/ops v0.0.0-20190325191751-d70cb0d6f85f // indirect - github.com/go-stack/stack v1.8.0 // indirect github.com/go-toast/toast v0.0.0-20190211030409-01e6764cf0a4 // indirect github.com/godbus/dbus/v5 v5.1.0 // indirect github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d // indirect - github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c // indirect github.com/tadvi/systray v0.0.0-20190226123456-11a2b8fa57af // indirect + github.com/tevino/abool v1.2.0 // indirect golang.org/x/text v0.14.0 // indirect ) diff --git a/go.sum b/go.sum index 3e360c7..e0d5bd3 100644 --- a/go.sum +++ b/go.sum @@ -4,6 +4,8 @@ crawshaw.io/iox v0.0.0-20181124134642-c51c3df30797/go.mod h1:sXBiorCo8c46JlQV3oX crawshaw.io/sqlite v0.3.2/go.mod h1:igAO5JulrQ1DbdZdtVq48mnZUBAPOeFzer7VhDWNtW4= filippo.io/edwards25519 v1.0.0-rc.1 h1:m0VOOB23frXZvAOK44usCgLWvtsxIoMCTBGJZlpmGfU= filippo.io/edwards25519 v1.0.0-rc.1/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns= +fyne.io/systray v1.10.0 h1:Yr1D9Lxeiw3+vSuZWPlaHC8BMjIHZXJKkek706AfYQk= +fyne.io/systray v1.10.0/go.mod h1:oM2AQqGJ1AMo4nNqZFYU8xYygSBZkW2hmdJ7n4yjedE= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/RoaringBitmap/roaring v0.4.7/go.mod h1:8khRDP4HmeXns4xIj9oGrKSz7XTQiJx2zgh7AcNke4w= github.com/RoaringBitmap/roaring v0.4.17/go.mod h1:D3qVegWTmfCaX4Bl5CrBE9hfrSrrXIr8KVNvRsDi1NI= @@ -129,20 +131,6 @@ github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uq github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= github.com/gen2brain/beeep v0.0.0-20240112042604-c7bb2cd88fea h1:oWUHxzaBvwkRWiINbBOY39XIF+n9b4RJEPHdQ8waJUo= github.com/gen2brain/beeep v0.0.0-20240112042604-c7bb2cd88fea/go.mod h1:0W7dI87PvXJ1Sjs0QPvWXKcQmNERY77e8l7GFhZB/s4= -github.com/getlantern/context v0.0.0-20190109183933-c447772a6520 h1:NRUJuo3v3WGC/g5YiyF790gut6oQr5f3FBI88Wv0dx4= -github.com/getlantern/context v0.0.0-20190109183933-c447772a6520/go.mod h1:L+mq6/vvYHKjCX2oez0CgEAJmbq1fbb/oNJIWQkBybY= -github.com/getlantern/errors v0.0.0-20190325191628-abdb3e3e36f7 h1:6uJ+sZ/e03gkbqZ0kUG6mfKoqDb4XMAzMIwlajq19So= -github.com/getlantern/errors v0.0.0-20190325191628-abdb3e3e36f7/go.mod h1:l+xpFBrCtDLpK9qNjxs+cHU6+BAdlBaxHqikB6Lku3A= -github.com/getlantern/golog v0.0.0-20190830074920-4ef2e798c2d7 h1:guBYzEaLz0Vfc/jv0czrr2z7qyzTOGC9hiQ0VC+hKjk= -github.com/getlantern/golog v0.0.0-20190830074920-4ef2e798c2d7/go.mod h1:zx/1xUUeYPy3Pcmet8OSXLbF47l+3y6hIPpyLWoR9oc= -github.com/getlantern/hex v0.0.0-20190417191902-c6586a6fe0b7 h1:micT5vkcr9tOVk1FiH8SWKID8ultN44Z+yzd2y/Vyb0= -github.com/getlantern/hex v0.0.0-20190417191902-c6586a6fe0b7/go.mod h1:dD3CgOrwlzca8ed61CsZouQS5h5jIzkK9ZWrTcf0s+o= -github.com/getlantern/hidden v0.0.0-20190325191715-f02dbb02be55 h1:XYzSdCbkzOC0FDNrgJqGRo8PCMFOBFL9py72DRs7bmc= -github.com/getlantern/hidden v0.0.0-20190325191715-f02dbb02be55/go.mod h1:6mmzY2kW1TOOrVy+r41Za2MxXM+hhqTtY3oBKd2AgFA= -github.com/getlantern/ops v0.0.0-20190325191751-d70cb0d6f85f h1:wrYrQttPS8FHIRSlsrcuKazukx/xqO/PpLZzZXsF+EA= -github.com/getlantern/ops v0.0.0-20190325191751-d70cb0d6f85f/go.mod h1:D5ao98qkA6pxftxoqzibIBBrLSUli+kYnJqrgBf9cIA= -github.com/getlantern/systray v1.2.2 h1:dCEHtfmvkJG7HZ8lS/sLklTH4RKUcIsKrAD9sThoEBE= -github.com/getlantern/systray v1.2.2/go.mod h1:pXFOI1wwqwYXEhLPm9ZGjS2u/vVELeIgNMY5HvhHhcE= github.com/glycerine/go-unsnap-stream v0.0.0-20180323001048-9f0cb55181dd/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE= github.com/glycerine/go-unsnap-stream v0.0.0-20181221182339-f9677308dec2/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE= github.com/glycerine/go-unsnap-stream v0.0.0-20190901134440-81cf024a9e0a/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE= @@ -164,11 +152,11 @@ github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/go-toast/toast v0.0.0-20190211030409-01e6764cf0a4 h1:qZNfIGkIANxGv/OqtnntR4DfOY2+BgwR60cAcu/i3SE= github.com/go-toast/toast v0.0.0-20190211030409-01e6764cf0a4/go.mod h1:kW3HQ4UdaAyrUCSSDR4xUzBKW6O2iA4uHhk7AtyYp10= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= @@ -239,8 +227,6 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/lxn/walk v0.0.0-20210112085537-c389da54e794/go.mod h1:E23UucZGqpuUANJooIbHWCufXvOcT6E7Stq81gU+CSQ= -github.com/lxn/win v0.0.0-20210218163916-a377121e959e/go.mod h1:KxxjdtRkfNoYDCUP5ryK7XJJNTnpC8atvtmTheChOtk= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= @@ -266,8 +252,6 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= -github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c h1:rp5dCmg/yLR3mgFuSOe4oEnDDmGLROTvMragMUXpTQw= -github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c/go.mod h1:X07ZCGwUbLaax7L0S3Tw4hpejzu63ZrrQiUe6W0hcy0= github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pion/datachannel v1.5.2 h1:piB93s8LGmbECrpO84DnkIVWasRMk3IimbcXkTQLE6E= @@ -354,7 +338,6 @@ github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46/go.mod h1:uAQ5P github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/assertions v0.0.0-20190215210624-980c5ac6f3ac/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s= @@ -376,6 +359,8 @@ github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKs github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/tadvi/systray v0.0.0-20190226123456-11a2b8fa57af h1:6yITBqGTE2lEeTPG04SN9W+iWHCRyHqlVYILiSXziwk= github.com/tadvi/systray v0.0.0-20190226123456-11a2b8fa57af/go.mod h1:4F09kP5F+am0jAwlQLddpoMDM+iewkxxt6nxUQ5nq5o= +github.com/tevino/abool v1.2.0 h1:heAkClL8H6w+mK5md9dzsuohKeXHUpY7Vw0ZCKW+huA= +github.com/tevino/abool v1.2.0/go.mod h1:qc66Pna1RiIsPa7O4Egxxs9OqkuxDX55zznh9K07Tzg= github.com/tidwall/btree v1.6.0 h1:LDZfKfQIBHGHWSwckhXI0RPSXzlo+KYdjK7FWSqOzzg= github.com/tidwall/btree v1.6.0/go.mod h1:twD9XRA5jj9VUQGELzDO4HPQTNJsoWWfYEL+EUQ2cKY= github.com/tinylib/msgp v1.0.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= @@ -472,9 +457,9 @@ golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200413165638-669c56c373c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201018230417-eeed37f84f13/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -538,7 +523,6 @@ google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzi google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -gopkg.in/Knetic/govaluate.v3 v3.0.0/go.mod h1:csKLBORsPbafmSCGTEh3U7Ozmsuq8ZSIlKk1bcqph0E= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/handler/handler.go b/handler/handler.go index 59fcbe4..72bd0da 100644 --- a/handler/handler.go +++ b/handler/handler.go @@ -2,6 +2,7 @@ package handler import ( "net/http" + "net/url" "github.com/gen2brain/beeep" "github.com/go-chi/chi/v5" @@ -42,14 +43,22 @@ func (h *Handler) Register(r chi.Router) { // watch torrent r.Get("/torrents/{infoHash}/watch/{fileName}", func(w http.ResponseWriter, r *http.Request) { infoHash := chi.URLParam(r, "infoHash") - fileName := chi.URLParam(r, "fileName") + fileName, err := url.QueryUnescape(chi.URLParam(r, "fileName")) + if err != nil { + handleError(w, r, "Unescape file name", err, http.StatusBadRequest) + return + } h.Watch(w, r, infoHash, fileName) }) // subtitle r.Post("/torrents/{infoHash}/select-subtitle/{fileName}", func(w http.ResponseWriter, r *http.Request) { infoHash := chi.URLParam(r, "infoHash") - fileName := chi.URLParam(r, "fileName") + fileName, err := url.QueryUnescape(chi.URLParam(r, "fileName")) + if err != nil { + handleError(w, r, "Unescape file name", err, http.StatusBadRequest) + return + } h.SelectSubtitle(w, r, infoHash, fileName) }) r.Delete("/torrents/{infoHash}/unset-subtitle", func(w http.ResponseWriter, r *http.Request) { @@ -68,10 +77,25 @@ func (h *Handler) Register(r chi.Router) { // stream torrent r.Get("/stream/{infoHash}/{fileName}", func(w http.ResponseWriter, r *http.Request) { infoHash := chi.URLParam(r, "infoHash") - fileName := chi.URLParam(r, "fileName") + fileName, err := url.QueryUnescape(chi.URLParam(r, "fileName")) + if err != nil { + handleError(w, r, "Unescape file name", err, http.StatusBadRequest) + return + } h.Stream(w, r, infoHash, fileName) }) + // open in vlc + r.Get("/open-in-vlc/{infoHash}/{fileName}", func(w http.ResponseWriter, r *http.Request) { + infoHash := chi.URLParam(r, "infoHash") + fileName, err := url.QueryUnescape(chi.URLParam(r, "fileName")) + if err != nil { + handleError(w, r, "Unescape file name", err, http.StatusBadRequest) + return + } + h.OpenInVLC(w, r, infoHash, fileName) + }) + // setting r.Post("/settings", h.UpdateSetting) diff --git a/handler/handler_video_player.go b/handler/handler_video_player.go index 6a8bfac..9ff1f59 100644 --- a/handler/handler_video_player.go +++ b/handler/handler_video_player.go @@ -2,20 +2,16 @@ package handler import ( "net/http" - "net/url" "time" "github.com/gabriel-vasile/mimetype" + "TorPlayer2/handler/uri" "TorPlayer2/ui" + "TorPlayer2/vlc" ) func (h *Handler) Watch(w http.ResponseWriter, r *http.Request, infoHash, fileName string) { - fileName, err := url.QueryUnescape(fileName) - if err != nil { - handleError(w, r, "Unescape file name", err, http.StatusBadRequest) - return - } torrentInfo, err := h.torrentManager.GetTorrentInfo(infoHash) if err != nil { handleError(w, r, "Get torrent", err, http.StatusBadRequest) @@ -54,3 +50,19 @@ func (h *Handler) Stream(w http.ResponseWriter, r *http.Request, infoHash, fileN http.ServeContent(w, r, file.DisplayPath(), time.Time{}, reader) } + +func (h *Handler) OpenInVLC(w http.ResponseWriter, r *http.Request, infoHash, fileName string) { + protocol := "http" + if r.TLS != nil { + protocol = "https" + } + + streamURL := protocol + "://" + r.Host + uri.StreamURI(infoHash, fileName) + + if err := vlc.OpenURL(streamURL); err != nil { + handleError(w, r, "Open in VLC", err, http.StatusBadRequest) + return + } + + w.WriteHeader(http.StatusNoContent) +} diff --git a/handler/uri/uri.go b/handler/uri/uri.go new file mode 100644 index 0000000..9bb4bb1 --- /dev/null +++ b/handler/uri/uri.go @@ -0,0 +1,19 @@ +// package uri provides URI generation helpers for the web interface. +package uri + +import ( + "fmt" + "net/url" +) + +func StreamURI(infoHash, fileName string) string { + return fmt.Sprintf("/stream/%s/%s", infoHash, url.QueryEscape(fileName)) +} + +func OpenInVLCURI(infoHash, fileName string) string { + return fmt.Sprintf("/open-in-vlc/%s/%s", infoHash, url.QueryEscape(fileName)) +} + +func WatchURI(infoHash, fileName string) string { + return fmt.Sprintf("/torrents/%s/watch/%s", infoHash, url.QueryEscape(fileName)) +} diff --git a/main.go b/main.go index 113f378..12bbb5a 100644 --- a/main.go +++ b/main.go @@ -9,7 +9,7 @@ import ( "net/http" "os" - "github.com/getlantern/systray" + "fyne.io/systray" "github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5/middleware" "github.com/pkg/browser" @@ -88,7 +88,7 @@ func setupOnReady(serverAddr string) func() { systray.SetIcon(appIcon) systray.SetTitle("TorPlayer") systray.SetTooltip("TorPlayer") - mOpenBrowser := systray.AddMenuItem("Open Torplay web", "Open Torplay in browser") + mOpenBrowser := systray.AddMenuItem("Open TorPlayer web", "Open TorPlayer in browser") systray.AddSeparator() mQuit := systray.AddMenuItem("Quit", "Quit the whole app") @@ -96,10 +96,7 @@ func setupOnReady(serverAddr string) func() { for { select { case <-mOpenBrowser.ClickedCh: - err := browser.OpenURL(serverAddr) - if err != nil { - slog.With("error", err).Error("Failed to open browser") - } + openURL(serverAddr) case <-mQuit.ClickedCh: systray.Quit() return @@ -107,7 +104,14 @@ func setupOnReady(serverAddr string) func() { } }() - _ = browser.OpenURL(serverAddr) + openURL(serverAddr) + } +} + +func openURL(url string) { + err := browser.OpenURL(url) + if err != nil { + slog.With("error", err).Error("Failed to open browser") } } diff --git a/ui/helper.go b/ui/helper.go index 8f8129f..6cc64c8 100644 --- a/ui/helper.go +++ b/ui/helper.go @@ -26,7 +26,7 @@ func percent(value, total int64) string { } func isVideoFile(filename string) bool { - return strings.HasSuffix(filename, ".mp4") + return strings.HasSuffix(filename, ".mp4") || strings.HasSuffix(filename, ".mkv") } var ( diff --git a/ui/info.templ b/ui/info.templ index c685a7c..768ab7f 100644 --- a/ui/info.templ +++ b/ui/info.templ @@ -1,9 +1,7 @@ package ui import "TorPlayer2/torrent" -import "strings" -import "fmt" -import "html" +import "TorPlayer2/handler/uri" templ Info(infoHash string, info torrent.Info) { @layout(info.Name) { @@ -29,15 +27,24 @@ templ Info(infoHash string, info torrent.Info) { { byteCounter(file.Length) } { percent(file.BytesCompleted, file.Length) } - if strings.HasSuffix(file.DisplayPath, ".mp4") { + if isVideoFile(file.DisplayPath) { - + Watch + + + Open in VLC + } diff --git a/ui/info_templ.go b/ui/info_templ.go index ef7c30f..3cc4e74 100644 --- a/ui/info_templ.go +++ b/ui/info_templ.go @@ -11,9 +11,7 @@ import "io" import "bytes" import "TorPlayer2/torrent" -import "strings" -import "fmt" -import "html" +import "TorPlayer2/handler/uri" func Info(infoHash string, info torrent.Info) templ.Component { return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) { @@ -46,7 +44,7 @@ func Info(infoHash string, info torrent.Info) templ.Component { var templ_7745c5c3_Var4 string templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(infoHash) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/info.templ`, Line: 10, Col: 50} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/info.templ`, Line: 8, Col: 50} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4)) if templ_7745c5c3_Err != nil { @@ -59,7 +57,7 @@ func Info(infoHash string, info torrent.Info) templ.Component { var templ_7745c5c3_Var5 string templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(info.Name) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/info.templ`, Line: 11, Col: 45} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/info.templ`, Line: 9, Col: 45} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5)) if templ_7745c5c3_Err != nil { @@ -77,7 +75,7 @@ func Info(infoHash string, info torrent.Info) templ.Component { var templ_7745c5c3_Var7 string templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(byteCounter(info.Length)) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/info.templ`, Line: 12, Col: 38} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/info.templ`, Line: 10, Col: 38} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7)) if templ_7745c5c3_Err != nil { @@ -131,7 +129,7 @@ func Info(infoHash string, info torrent.Info) templ.Component { var templ_7745c5c3_Var12 string templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(file.DisplayPath) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/info.templ`, Line: 27, Col: 47} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/info.templ`, Line: 25, Col: 47} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12)) if templ_7745c5c3_Err != nil { @@ -144,7 +142,7 @@ func Info(infoHash string, info torrent.Info) templ.Component { var templ_7745c5c3_Var13 string templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(byteCounter(file.Length)) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/info.templ`, Line: 28, Col: 66} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/info.templ`, Line: 26, Col: 66} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13)) if templ_7745c5c3_Err != nil { @@ -157,7 +155,7 @@ func Info(infoHash string, info torrent.Info) templ.Component { var templ_7745c5c3_Var14 string templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(percent(file.BytesCompleted, file.Length)) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/info.templ`, Line: 29, Col: 83} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/info.templ`, Line: 27, Col: 83} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14)) if templ_7745c5c3_Err != nil { @@ -167,17 +165,17 @@ func Info(infoHash string, info torrent.Info) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - if strings.HasSuffix(file.DisplayPath, ".mp4") { + if isVideoFile(file.DisplayPath) { _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" ") + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" class=\"rounded-sm bg-red-600 px-4 py-2 text-slate-100 hover:bg-red-700 shadow-md shadow-stone-900\"> ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -186,6 +184,24 @@ func Info(infoHash string, info torrent.Info) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Var18 := `Open in VLC` + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var18) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err diff --git a/ui/video_player.templ b/ui/video_player.templ index 8ef4f30..01d503d 100644 --- a/ui/video_player.templ +++ b/ui/video_player.templ @@ -4,6 +4,7 @@ import "fmt" import "net/url" import "TorPlayer2/torrent" import "TorPlayer2/subtitle" +import "TorPlayer2/handler/uri" func getSubtitleFiles(info torrent.Info) []torrent.File { var files []torrent.File @@ -27,7 +28,7 @@ templ VideoPlayer(info torrent.Info, fileName string) { crossorigin="anonymous" class="w-full shadow-sm shadow-stone-900" > - +
@subtitleController(info, subtitle.State{}) diff --git a/ui/video_player_templ.go b/ui/video_player_templ.go index 41aafcc..d8239ad 100644 --- a/ui/video_player_templ.go +++ b/ui/video_player_templ.go @@ -14,6 +14,7 @@ import "fmt" import "net/url" import "TorPlayer2/torrent" import "TorPlayer2/subtitle" +import "TorPlayer2/handler/uri" func getSubtitleFiles(info torrent.Info) []torrent.File { var files []torrent.File @@ -51,7 +52,7 @@ func VideoPlayer(info torrent.Info, fileName string) templ.Component { var templ_7745c5c3_Var3 string templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(fileName) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/video_player.templ`, Line: 20, Col: 39} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/video_player.templ`, Line: 21, Col: 39} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3)) if templ_7745c5c3_Err != nil { @@ -61,7 +62,7 @@ func VideoPlayer(info torrent.Info, fileName string) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(fmt.Sprintf("/stream/%s/%s", info.InfoHash, url.QueryEscape(fileName)))) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(uri.StreamURI(info.InfoHash, fileName))) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -150,7 +151,7 @@ func subtitleController(torrentInfo torrent.Info, state subtitle.State) templ.Co var templ_7745c5c3_Var7 string templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(state.Name) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/video_player.templ`, Line: 48, Col: 39} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/video_player.templ`, Line: 49, Col: 39} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7)) if templ_7745c5c3_Err != nil { @@ -222,7 +223,7 @@ func subtitleController(torrentInfo torrent.Info, state subtitle.State) templ.Co var templ_7745c5c3_Var11 string templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(toString(state.AdjustmentMilliseconds)) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/video_player.templ`, Line: 77, Col: 61} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/video_player.templ`, Line: 78, Col: 61} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11)) if templ_7745c5c3_Err != nil { @@ -302,7 +303,7 @@ func subtitleController(torrentInfo torrent.Info, state subtitle.State) templ.Co var templ_7745c5c3_Var16 string templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinStringErrs(file.DisplayPath) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/video_player.templ`, Line: 115, Col: 26} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/video_player.templ`, Line: 116, Col: 26} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var16)) if templ_7745c5c3_Err != nil { @@ -328,7 +329,7 @@ func subtitleController(torrentInfo torrent.Info, state subtitle.State) templ.Co var templ_7745c5c3_Var17 string templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinStringErrs(file.DisplayPath) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/video_player.templ`, Line: 124, Col: 26} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/video_player.templ`, Line: 125, Col: 26} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var17)) if templ_7745c5c3_Err != nil { diff --git a/vlc/open.go b/vlc/open.go new file mode 100644 index 0000000..1cb7ec8 --- /dev/null +++ b/vlc/open.go @@ -0,0 +1,5 @@ +package vlc + +func OpenURL(url string) error { + return open(url) +} diff --git a/vlc/open_darwin.go b/vlc/open_darwin.go new file mode 100644 index 0000000..a0cce90 --- /dev/null +++ b/vlc/open_darwin.go @@ -0,0 +1,12 @@ +//go:build !ios + +package vlc + +import ( + "os/exec" +) + +func open(url string) error { + cmd := exec.Command("open", url, "-a", "VLC") + return cmd.Run() +} diff --git a/vlc/open_unix.go b/vlc/open_unix.go new file mode 100644 index 0000000..e2fb7f9 --- /dev/null +++ b/vlc/open_unix.go @@ -0,0 +1,12 @@ +//go:build (linux || freebsd || openbsd || netbsd) && !android + +package vlc + +import ( + "os/exec" +) + +func open(url string) error { + cmd := exec.Command("vlc", url) + return cmd.Run() +} diff --git a/vlc/open_windows.go b/vlc/open_windows.go new file mode 100644 index 0000000..2c1ec2a --- /dev/null +++ b/vlc/open_windows.go @@ -0,0 +1,12 @@ +//go:build windows + +package vlc + +import ( + "os/exec" +) + +func open(url string) error { + cmd := exec.Command(`C:\Program Files\VideoLAN\VLC\vlc.exe`, url) + return cmd.Run() +}