diff --git a/README.md b/README.md index 1dcf1c0..60ff840 100644 --- a/README.md +++ b/README.md @@ -3,15 +3,32 @@ Proxy Server for older Samsung Smart-TVs (E/F - Series) which only support TLS u ## Installation +### Preparing the Proxy + +Download the corresponding binary for your system from the [latest releases](https://github.com/kadrim/proxy4plex/releases/latest) and run it. You might want to run the binary on system start. This is different for each system but in the future the proxy will install itself if needed. + ### Preparing the Smart-TV -- Download the latest version of Orca's Plex-App for Samsung TVs from here https://www.dropbox.com/s/f17hx2w7tvofjqr/Plex_2.014_11112020.zip?dl=1 -- Install it using USB-Sideloading or via SammyWidgets -- Check this thread if you need more information: https://forums.plex.tv/t/samsung-tv-cannot-connect-to-plex/650100/8 +#### Using proxy4plex as sideloader -### Preparing the Proxy +Notice: this will only work, if the system you are running on, is on the same network as your TV and has not yet a service running on Port 80, because this is needed by the TV to download new Apps. I.e: This will not work on non-rooted Android Phones, as the Port 80 is blocked. Running the proxy on non-rooted Android phones does work. So the initial Setup has do be done via another machine. -Download the corresponding binary for your system from the [latest releases](https://github.com/kadrim/proxy4plex/releases/latest) and run it. You might want to run the binary on system start. This is different for each system but in the future the proxy will install itself if needed. +This process has only to be done once. So after the app has been installed via this method, it stays on the TV! + +1. Start the `proxy4plex` binary +2. Open `smarthub` on your TV +3. Hit the `Red` button on the remote to login to an account +4. Use `develop` for username, password should fill automatically, if it does not, use `111111` +5. Hit the `Tools` button on the remote and select `Settings` while within `smarthub` +6. Select the menu-entry `Development` and accept the agreement after reading it +7. Select `Server-IP-Settings` and enter the local IP-Adress of your machine running `proxy4plex` +8. Select `Synchronize` and the app will be installed permanentely on the TV + +#### Manual sideloading + +1. Download the latest version of Orca's Plex-App for Samsung TVs from here https://www.dropbox.com/s/f17hx2w7tvofjqr/Plex_2.014_11112020.zip?dl=1 +2. Install it using USB-Sideloading or via SammyWidgets +3. Check this thread if you need more information: https://forums.plex.tv/t/samsung-tv-cannot-connect-to-plex/650100/8 ### Putting it together @@ -38,6 +55,8 @@ On Android this package is currently not yet available as native app. Neverthele After that you can use the IP-Adress of your android device for your Plex-App on the TV. If you close termux or reboot your device you can simply re-run the proxy by starting termux and then starting the proxy via the command `proxy4plex/proxy4plex` +Beware: Sideloading (i.e. installing the app on the TV) does not work this way, because non-rooted Android devices are not allowed to use Port 80 + ## Compiling At the time of writing, this package needs at least [golang](https://golang.org/) v1.15 @@ -51,7 +70,6 @@ To compile for all possible architectures you can run the command `go-build-all. ## TODOs - detect OS and allow User to install the proxy as a boot-service -- serve as sideloading server so no need to manually run SammyWidgets - porting to Android and iOS as native apps - auto-update notifications diff --git a/main.go b/main.go index 8b480e9..02473bf 100644 --- a/main.go +++ b/main.go @@ -1,17 +1,32 @@ package main import ( + "archive/zip" + "bytes" + "crypto/sha256" + "encoding/hex" + "errors" + "fmt" + "io/ioutil" "log" "net/http" "net/http/httputil" "net/url" + "regexp" + "strconv" ) const ( - port = "3000" - server = "http://plex.tv" + port = "3000" + server = "http://plex.tv" + officialAppURL = "https://www.dropbox.com/s/f17hx2w7tvofjqr/Plex_2.014_11112020.zip?dl=1" + officialAppChksum = "8c6b2bb25a4c2492fd5dbde885946dcb6b781ba292e5038239559fd7a20e707e" + modifiedAppName = "Plex_2.014_net" + modifiedAppFile = "Plex_2.014_11112020_net.zip" ) +var modifiedBuffer []byte + type transport struct { http.RoundTripper } @@ -66,15 +81,140 @@ func handleRequest(res http.ResponseWriter, req *http.Request) { proxy.ServeHTTP(res, req) } +func extractZipFile(zipFile *zip.File) ([]byte, error) { + file, err := zipFile.Open() + if err != nil { + return nil, err + } + defer file.Close() + return ioutil.ReadAll(file) +} + +func retreiveZipFile() ([]byte, error) { + // only retreive data once + if modifiedBuffer != nil { + return modifiedBuffer, nil + } + + // download the original zip + resp, err := http.Get(officialAppURL) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + + // checksum + chksum := sha256.Sum256(body) + if hex.EncodeToString(chksum[:]) == officialAppChksum { + log.Println("Checksum match. Going on ...") + } else { + err := errors.New("Checksum did not match, aborting") + return nil, err + } + + // attach the zipWriter to the buffer + buf := new(bytes.Buffer) + w := zip.NewWriter(buf) + + // create zipReader + zipReader, err := zip.NewReader(bytes.NewReader(body), int64(len(body))) + if err != nil { + return nil, err + } + + // regexp pattern to remove first directory + regex := regexp.MustCompile(`^Plex/`) + + // process all files, read each file and write it to new zip + for _, zipFile := range zipReader.File { + fmt.Println("Processing file:", zipFile.Name) + newFileName := regex.ReplaceAllString(zipFile.Name, "") + fmt.Println("Replacement:", newFileName) + bytes, err := extractZipFile(zipFile) + if err != nil { + return nil, err + } + + // write file to new zip + file, err := w.Create(newFileName) + if err != nil { + return nil, err + } + _, err = file.Write(bytes) + if err != nil { + return nil, err + } + } + + err = w.Close() + if err != nil { + return nil, err + } + + // write data to global var + modifiedBuffer = buf.Bytes() + + return modifiedBuffer, nil +} + func main() { // handle simple information path http.HandleFunc("/info", func(res http.ResponseWriter, req *http.Request) { - res.Write([]byte("The Plex proxy service is running")) + res.Write([]byte("The Plex proxy service is running on " + req.Host)) }) - // start real server + //handle widgetlist for sideloading + http.HandleFunc("/widgetlist.xml", func(res http.ResponseWriter, req *http.Request) { + buf, err := retreiveZipFile() + if err != nil { + res.WriteHeader(http.StatusInternalServerError) + res.Write([]byte(err.Error())) + log.Println(err) + return + } + xml := ` + + + + Plex + + + http://` + req.Host + `/` + modifiedAppFile + ` + + + ` + + res.Write([]byte(xml)) + }) + + // handle app-deployment + http.HandleFunc("/"+modifiedAppFile, func(res http.ResponseWriter, req *http.Request) { + buf, err := retreiveZipFile() + if err != nil { + res.WriteHeader(http.StatusInternalServerError) + res.Write([]byte(err.Error())) + log.Println(err) + return + } + // write the http-response + res.Write(buf) + }) + + // start real proxy http.HandleFunc("/", handleRequest) + // try to handle everything on port 80 aswell for serving the app + // Note: this will not work on non-rooted android because only high-ports can be used + go func() { + log.Println("Trying to start app-deployer on port 80 ...") + http.ListenAndServe(":80", nil) + }() + log.Println("Server starting on Port " + port + " ...") log.Fatal(http.ListenAndServe(":"+port, nil)) }