Files
bs-patch/main.go
David Suarez 91648e41b3 Initial Commit
2020-11-27 15:22:45 -08:00

246 lines
6.4 KiB
Go

package main
import (
"bytes"
"fmt"
"github.com/dustin/go-humanize"
log "github.com/sirupsen/logrus"
"github.com/spf13/viper"
"io"
"io/ioutil"
"net/http"
"os"
"os/exec"
"regexp"
"strings"
)
func main() {
log.Info("Reading config.yaml")
viper.AddConfigPath(".")
viper.SetConfigName("config")
viper.SetConfigType("yaml")
err := viper.ReadInConfig()
if err != nil {
log.Fatalf("Error reading config.yaml: %s", err.Error())
}
if viper.GetString("nodeBinPath") == "" {
log.Fatal("nodeBinPath not set in config.yaml")
}
currentPath := os.Getenv("PATH")
os.Setenv("PATH", fmt.Sprintf("%s:%s", currentPath, viper.GetString("nodeBinPath")))
log.Info("Finding Latest BluOS Controller")
l, err := getLatest()
if err != nil {
log.Fatalf("Error getting latest client URL: %s", err.Error())
}
log.Infof("Downloading Latest BlueOS controller: %s", *l)
err = downloadFile("./controller.dmg", *l)
if err != nil {
log.Fatalf("Error downloading latest controller dmg: %s", err.Error())
}
log.Info("Extracting BlueOS Controller DMG")
extractController()
log.Info("Extracting ASAR")
extractAsar()
log.Info("Patching electron.js")
patchFile(
"./bluos/www/js/electron.js",
` if(platform=='mac'){
path = "/Applications/Spotify.app"
}`,
` if(platform=='mac'){
path = "/Applications/Spotify.app"
}
if(platform=='lin64') { path = "/snap/bin/spotify" }`)
log.Info("Patching app.js - Update Check")
patchFile("./bluos/www/app.js",
`f7.checkAppUpdate=function(e){var t;"macOS"==f7.appInfo.platform&&(t="http://upgrade.nadelectronics.com/desktop_app/osx/version.xml?currentVersion"),"Windows"==f7.appInfo.platform&&(t="http://upgrade.nadelectronics.com/desktop_app/windows/version.xml?currentVersion")`,
`f7.checkAppUpdate=function(e){var t;"linux" == f7.appInfo.platform && (t = "http://upgrade.nadelectronics.com/desktop_app/osx/version.xml?currentVersion"), "macOS"==f7.appInfo.platform&&(t="http://upgrade.nadelectronics.com/desktop_app/osx/version.xml?currentVersion"),"Windows"==f7.appInfo.platform&&(t="http://upgrade.nadelectronics.com/desktop_app/windows/version.xml?currentVersion")`)
log.Info("Patching app.js - Update Platform")
patchFile("./bluos/www/app.js",
`.autoupgrade,queue:{loading:!1,pagesize:200,total:0}}`,
`.autoupgrade,queue:{loading:!1,pagesize:200,total:0},platform: "linux"}`)
log.Info("Adding electron dependency")
addNpmPackage("electron@^9.0.0")
log.Info("Adding electron-builder dependency")
addNpmPackage("electron-builder")
log.Info("Building Snap and AppImage")
buildPackage()
log.Info("Cleaning up")
cleanUp()
}
func cleanUp() {
os.Rename("./bluos/dist", "./dist")
os.RemoveAll("./bluos")
os.RemoveAll("./controller")
os.Remove("./controller.dmg")
}
func addNpmPackage(packageName string) error {
cmd := exec.Command(fmt.Sprintf("%s/npm", viper.GetString("nodeBinPath")), "install", packageName, "--save-dev")
cmd.Dir = "./bluos"
var out bytes.Buffer
var eout bytes.Buffer
cmd.Stdout = &out
cmd.Stderr = &eout
err := cmd.Run()
if err != nil {
log.Errorf("Error installing electron: %s", eout.String())
return err
}
return nil
}
func buildPackage() error {
cmd := exec.Command(fmt.Sprintf("%s/electron-builder", viper.GetString("nodeBinPath")))
cmd.Dir = "./bluos"
var out bytes.Buffer
var eout bytes.Buffer
cmd.Stdout = &out
cmd.Stderr = &eout
err := cmd.Run()
if err != nil {
log.Errorf("Error extracting ASAR: %s", eout.String())
return err
}
return nil
}
func patchFile(filePath, search, replace string) error {
input, err := ioutil.ReadFile(filePath)
if err != nil {
return err
}
output := strings.Replace(string(input), search, replace, 1)
err = ioutil.WriteFile(filePath, []byte(output), 0)
if err != nil {
return err
}
return nil
}
func extractAsar() error {
folders, err := ioutil.ReadDir("./controller/")
if err != nil {
return err
}
for _, f := range folders {
if strings.Contains(f.Name(), "BluOS Controller") {
cmd := exec.Command(fmt.Sprintf("%s/npx", viper.GetString("nodeBinPath")), "asar", "extract", fmt.Sprintf("./controller/%s/BluOS Controller.app/Contents/Resources/app.asar", f.Name()), "./bluos")
var out bytes.Buffer
var eout bytes.Buffer
cmd.Stdout = &out
cmd.Stderr = &eout
err := cmd.Run()
if err != nil {
log.Errorf("Error extracting ASAR: %s", eout.String())
return err
}
}
}
return nil
}
func extractController() {
c := exec.Command("7z", "x", "-ocontroller", "./controller.dmg")
c.Run()
}
func getLatest() (*string, error) {
resp, err := http.Get("https://www.bluesound.com/downloads/")
if err != nil {
log.Errorf("Error getting Bluesound download page: %s", err.Error())
return nil, err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Errorf("Error reading response body")
return nil, err
}
re := regexp.MustCompile(`(http|ftp|https)://([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,@?^=%&:/~+#-]*[\w@?^=%&/~+#-])?.dmg`)
url := string(re.Find(body))
return &url, nil
}
type WriteCounter struct {
Total uint64
}
func (wc *WriteCounter) Write(p []byte) (int, error) {
n := len(p)
wc.Total += uint64(n)
wc.PrintProgress()
return n, nil
}
func (wc WriteCounter) PrintProgress() {
// Clear the line by using a character return to go back to the start and remove
// the remaining characters by filling it with spaces
fmt.Printf("\r%s", strings.Repeat(" ", 35))
// Return again and print current status of download
// We use the humanize package to print the bytes in a meaningful way (e.g. 10 MB)
fmt.Printf("\rDownloading... %s complete", humanize.Bytes(wc.Total))
}
func downloadFile(filepath string, url string) error {
// Create the file, but give it a tmp file extension, this means we won't overwrite a
// file until it's downloaded, but we'll remove the tmp extension once downloaded.
out, err := os.Create(filepath + ".tmp")
if err != nil {
return err
}
// Get the data
resp, err := http.Get(url)
if err != nil {
out.Close()
return err
}
defer resp.Body.Close()
// Create our progress reporter and pass it to be used alongside our writer
counter := &WriteCounter{}
if _, err = io.Copy(out, io.TeeReader(resp.Body, counter)); err != nil {
out.Close()
return err
}
// The progress use the same line so print a new line once it's finished downloading
fmt.Print("\n")
// Close the file without defer so it can happen before Rename()
out.Close()
if err = os.Rename(filepath+".tmp", filepath); err != nil {
return err
}
return nil
}