Add updater

pull/3/head
lawl 5 years ago
parent 9d406a600c
commit b11e2e69dc

@ -20,6 +20,8 @@ release: rnnoise
cd tmp/; \
tar cvzf ../bin/NoiseTorch_x64.tgz .
rm -rf tmp/
go run scripts/signer.go -s
git describe --tags > bin/version.txt
rnnoise:
cd librnnoise_ladspa/; \
cmake . -DBUILD_VST_PLUGIN=OFF -DBUILD_LV2_PLUGIN=OFF -DBUILD_LADSPA_PLUGIN=ON; \

@ -15,7 +15,7 @@ Don't forget to ~~like, comment and subscribe~~ leave a star ⭐ if this sounds
* Two click setup of your virtual denoising microphone
* A single, small, statically linked, self-contained binary
## Download
## Download & Install
[Download the latest release from GitHub](https://github.com/lawl/NoiseTorch/releases).
@ -32,6 +32,12 @@ With gnome this can be done with:
You now have a `noisetorch` binary and desktop entry on your system.
#### Uninstall
rm .local/bin/noisetorch
rm .local/share/applications/noisetorch.desktop
rm .local/share/icons/hicolor/256x256/apps/noisetorch.png
## Usage
Select the microphone you want to denoise, and click "Load NoiseTorch", NoiseTorch will create a virtual microphone called "NoiseTorch Microphone" that you can select in any application.
@ -66,6 +72,9 @@ Install the Go compiler from [golang.org](https://golang.org/). And make sure yo
make # build it
```
If you build from source, it's recommended that you disable automatic update checks.
In `~/.config/noisetorch/config.toml` set `EnableUpdates = false`.
## Special thanks to
* [xiph.org](https://xiph.org)/[Mozilla's](https://mozilla.org) excellent [RNNoise](https://jmvalin.ca/demo/rnnoise/).

@ -13,6 +13,7 @@ import (
type config struct {
Threshold int
DisplayMonitorSources bool
EnableUpdates bool
}
const configDir = ".config/noisetorch/"
@ -20,7 +21,7 @@ const configFile = "config.toml"
func initializeConfigIfNot() {
log.Println("Checking if config needs to be initialized")
conf := config{Threshold: 95, DisplayMonitorSources: false}
conf := config{Threshold: 95, DisplayMonitorSources: false, EnableUpdates: true}
configdir := filepath.Join(os.Getenv("HOME"), configDir)
ok, err := exists(configdir)
if err != nil {

@ -28,7 +28,7 @@ type input struct {
func main() {
f, err := os.OpenFile("/tmp/noisetorch.log", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644)
f, err := os.OpenFile("/tmp/noisetorch.log", os.O_RDWR|os.O_CREATE, 0644)
if err != nil {
log.Fatalf("error opening file: %v\n", err)
}
@ -46,6 +46,10 @@ func main() {
ui.config = readConfig()
ui.librnnoise = rnnoisefile
if ui.config.EnableUpdates {
go updateCheck(&ui)
}
paClient, err := pulseaudio.NewClient()
defer paClient.Close()

28
ui.go

@ -25,8 +25,13 @@ type uistate struct {
versionScreen bool
licenseTextArea nucular.TextEditor
masterWindow *nucular.MasterWindow
update updateui
}
var green = color.RGBA{34, 187, 69, 255}
var red = color.RGBA{255, 70, 70, 255}
var orange = color.RGBA{255, 140, 0, 255}
func updatefn(w *nucular.Window, ui *uistate) {
if ui.loadingScreen {
@ -46,7 +51,7 @@ func updatefn(w *nucular.Window, ui *uistate) {
w.MenubarBegin()
w.Row(10).Dynamic(1)
w.Row(10).Dynamic(2)
if w := w.Menu(label.TA("About", "LC"), 120, nil); w != nil {
w.Row(10).Dynamic(1)
if w.MenuItem(label.T("Licenses")) {
@ -65,11 +70,26 @@ func updatefn(w *nucular.Window, ui *uistate) {
w.Row(15).Dynamic(1)
if ui.noiseSupressorState == loaded {
w.LabelColored("NoiseTorch active", "RC", color.RGBA{34, 187, 69, 255} /*green*/)
w.LabelColored("NoiseTorch active", "RC", green)
} else if ui.noiseSupressorState == unloaded {
w.LabelColored("NoiseTorch inactive", "RC", color.RGBA{255, 70, 70, 255} /*red*/)
w.LabelColored("NoiseTorch inactive", "RC", red)
} else if ui.noiseSupressorState == inconsistent {
w.LabelColored("Inconsistent state, please unload first.", "RC", color.RGBA{255, 140, 0, 255} /*orange*/)
w.LabelColored("Inconsistent state, please unload first.", "RC", orange)
}
if ui.update.available && !ui.update.triggered {
w.Row(20).Ratio(0.9, 0.1)
w.LabelColored("Update available! Click to install version: "+ui.update.serverVersion, "LC", green)
if w.ButtonText("Update") {
ui.update.triggered = true
go update(ui)
(*ui.masterWindow).Changed()
}
}
if ui.update.triggered {
w.Row(20).Dynamic(1)
w.Label(ui.update.updatingText, "CC")
}
if w.TreePush(nucular.TreeTab, "Settings", true) {

@ -0,0 +1,170 @@
/*
Copyright (c) 2009 The Go Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
// This is yoinked from https://github.com/golang/build/blob/master/internal/untar/untar.go
// which is unfortunately an internal package we can't import, but it's exactly what we need.
// So just copy paste it.
package main
import (
"archive/tar"
"compress/gzip"
"fmt"
"io"
"log"
"os"
"path"
"path/filepath"
"strings"
"time"
)
// TODO(bradfitz): this was copied from x/build/cmd/buildlet/buildlet.go
// but there were some buildlet-specific bits in there, so the code is
// forked for now. Unfork and add some opts arguments here, so the
// buildlet can use this code somehow.
// Untar reads the gzip-compressed tar file from r and writes it into dir.
func Untar(r io.Reader, dir string) error {
return untar(r, dir)
}
func untar(r io.Reader, dir string) (err error) {
t0 := time.Now()
nFiles := 0
madeDir := map[string]bool{}
defer func() {
td := time.Since(t0)
if err == nil {
log.Printf("extracted tarball into %s: %d files, %d dirs (%v)", dir, nFiles, len(madeDir), td)
} else {
log.Printf("error extracting tarball into %s after %d files, %d dirs, %v: %v", dir, nFiles, len(madeDir), td, err)
}
}()
zr, err := gzip.NewReader(r)
if err != nil {
return fmt.Errorf("requires gzip-compressed body: %v", err)
}
tr := tar.NewReader(zr)
loggedChtimesError := false
for {
f, err := tr.Next()
if err == io.EOF {
break
}
if err != nil {
log.Printf("tar reading error: %v", err)
return fmt.Errorf("tar error: %v", err)
}
if !validRelPath(f.Name) {
return fmt.Errorf("tar contained invalid name error %q", f.Name)
}
rel := filepath.FromSlash(f.Name)
abs := filepath.Join(dir, rel)
fi := f.FileInfo()
mode := fi.Mode()
switch {
case mode.IsRegular():
// Make the directory. This is redundant because it should
// already be made by a directory entry in the tar
// beforehand. Thus, don't check for errors; the next
// write will fail with the same error.
dir := filepath.Dir(abs)
if !madeDir[dir] {
if err := os.MkdirAll(filepath.Dir(abs), 0755); err != nil {
return err
}
madeDir[dir] = true
}
wf, err := os.OpenFile(abs, os.O_RDWR|os.O_CREATE|os.O_TRUNC, mode.Perm())
if err != nil {
return err
}
n, err := io.Copy(wf, tr)
if closeErr := wf.Close(); closeErr != nil && err == nil {
err = closeErr
}
if err != nil {
return fmt.Errorf("error writing to %s: %v", abs, err)
}
if n != f.Size {
return fmt.Errorf("only wrote %d bytes to %s; expected %d", n, abs, f.Size)
}
modTime := f.ModTime
if modTime.After(t0) {
// Clamp modtimes at system time. See
// golang.org/issue/19062 when clock on
// buildlet was behind the gitmirror server
// doing the git-archive.
modTime = t0
}
if !modTime.IsZero() {
if err := os.Chtimes(abs, modTime, modTime); err != nil && !loggedChtimesError {
// benign error. Gerrit doesn't even set the
// modtime in these, and we don't end up relying
// on it anywhere (the gomote push command relies
// on digests only), so this is a little pointless
// for now.
log.Printf("error changing modtime: %v (further Chtimes errors suppressed)", err)
loggedChtimesError = true // once is enough
}
}
nFiles++
case mode.IsDir():
if err := os.MkdirAll(abs, 0755); err != nil {
return err
}
madeDir[abs] = true
default:
return fmt.Errorf("tar file entry %s contained unsupported file type %v", f.Name, mode)
}
}
return nil
}
func validRelativeDir(dir string) bool {
if strings.Contains(dir, `\`) || path.IsAbs(dir) {
return false
}
dir = path.Clean(dir)
if strings.HasPrefix(dir, "../") || strings.HasSuffix(dir, "/..") || dir == ".." {
return false
}
return true
}
func validRelPath(p string) bool {
if p == "" || strings.Contains(p, `\`) || strings.HasPrefix(p, "/") || strings.Contains(p, "../") {
return false
}
return true
}

@ -0,0 +1,96 @@
package main
import (
"bytes"
"crypto/ed25519"
"encoding/base64"
"fmt"
"io/ioutil"
"log"
"net/http"
"os"
"strings"
)
var updateURL = "https://noisetorch.epicgamer.org"
var publicKeyString = "3mL+rBi4yBZ1wGimQ/oSQCjxELzgTh+673H4JdzQBOk="
type updateui struct {
serverVersion string
available bool
triggered bool
updatingText string
}
func updateCheck(ui *uistate) {
log.Println("Checking for updates")
bodybuf, err := fetchFile("version.txt")
if err != nil {
log.Println("Couldn't fetch version", err)
return
}
body := strings.TrimSpace(string(bodybuf))
ui.update.serverVersion = body
ui.update.available = true
}
func update(ui *uistate) {
sig, err := fetchFile("NoiseTorch_x64.tgz.sig")
if err != nil {
log.Println("Couldn't fetch signature", err)
ui.update.updatingText = "Update failed!"
(*ui.masterWindow).Changed()
return
}
tgz, err := fetchFile("NoiseTorch_x64.tgz")
if err != nil {
log.Println("Couldn't fetch tgz", err)
ui.update.updatingText = "Update failed!"
(*ui.masterWindow).Changed()
return
}
verified := ed25519.Verify(publickey(), tgz, sig)
log.Printf("VERIFIED UPDATE: %t\n", verified)
if !verified {
log.Printf("SIGNATURE VERIFICATION FAILED, ABORTING UPDATE!\n")
ui.update.updatingText = "Update failed!"
(*ui.masterWindow).Changed()
return
}
untar(bytes.NewReader(tgz), os.Getenv("HOME"))
log.Printf("Update installed!\n")
ui.update.updatingText = "Update installed!"
(*ui.masterWindow).Changed()
}
func fetchFile(file string) ([]byte, error) {
resp, err := http.Get(updateURL + "/" + file)
if err != nil {
return nil, err
}
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("received on 200 status code when fetching %s. Status: %s", file, resp.Status)
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
return body, nil
}
func publickey() []byte {
pub, err := base64.StdEncoding.DecodeString(publicKeyString)
if err != nil {
panic(err) // it's hardcoded, we should never hit this, panic if we do
}
return pub
}
Loading…
Cancel
Save