stash/pkg/desktop/desktop.go
kermieisinthehouse 0e514183a7
Desktop integration (#2073)
* Open stash in system tray on Windows/MacOS
* Add desktop notifications
* MacOS Bundling
* Add binary icon

Co-authored-by: WithoutPants <53250216+WithoutPants@users.noreply.github.com>
2022-02-03 11:20:34 +11:00

170 lines
3.8 KiB
Go

package desktop
import (
"io/ioutil"
"os"
"os/exec"
"path"
"path/filepath"
"runtime"
"strconv"
"strings"
"github.com/pkg/browser"
"github.com/stashapp/stash/pkg/logger"
"github.com/stashapp/stash/pkg/manager/config"
"github.com/stashapp/stash/pkg/utils"
"golang.org/x/term"
)
type ShutdownHandler interface {
Shutdown(code int)
}
type FaviconProvider interface {
GetFavicon() []byte
GetFaviconPng() []byte
}
func Start(shutdownHandler ShutdownHandler, faviconProvider FaviconProvider) {
if IsDesktop() {
c := config.GetInstance()
if !c.GetNoBrowser() {
openURLInBrowser("")
}
writeStashIcon(faviconProvider)
startSystray(shutdownHandler, faviconProvider)
}
}
// openURLInBrowser opens a browser to the Stash UI. Path can be an empty string for main page.
func openURLInBrowser(path string) {
// This can be done before actually starting the server, as modern browsers will
// automatically reload the page if a local port is closed at page load and then opened.
serverAddress := getServerURL(path)
err := browser.OpenURL(serverAddress)
if err != nil {
logger.Error("Could not open browser: " + err.Error())
}
}
func SendNotification(title string, text string) {
if IsDesktop() {
c := config.GetInstance()
if c.GetNotificationsEnabled() {
sendNotification(title, text)
}
}
}
func IsDesktop() bool {
// Check if running under root
if os.Getuid() == 0 {
return false
}
// Check if stdin is a terminal
if term.IsTerminal(int(os.Stdin.Fd())) {
return false
}
if isService() {
return false
}
if IsServerDockerized() {
return false
}
return true
}
func IsServerDockerized() bool {
return isServerDockerized()
}
// Set a command to execute in the background, instead of spawning a shell window
func HideExecShell(cmd *exec.Cmd) {
hideExecShell(cmd)
}
// writeStashIcon writes the current stash logo to config/icon.png
func writeStashIcon(faviconProvider FaviconProvider) {
c := config.GetInstance()
if !c.IsNewSystem() {
iconPath := path.Join(c.GetConfigPath(), "icon.png")
err := ioutil.WriteFile(iconPath, faviconProvider.GetFaviconPng(), 0644)
if err != nil {
logger.Errorf("Couldn't write icon file: %s", err.Error())
}
}
}
// IsAllowedAutoUpdate tries to determine if the stash binary was installed from a
// package manager or if touching the executable is otherwise a bad idea
func IsAllowedAutoUpdate() bool {
// Only try to update if downloaded from official sources
if !config.IsOfficialBuild() {
return false
}
// Avoid updating if installed from package manager
if runtime.GOOS == "linux" {
executablePath, err := os.Executable()
if err != nil {
logger.Errorf("Cannot get executable path: %s", err)
return false
}
executablePath, err = filepath.EvalSymlinks(executablePath)
if err != nil {
logger.Errorf("Cannot get executable path: %s", err)
return false
}
if utils.IsPathInDir("/usr", executablePath) || utils.IsPathInDir("/opt", executablePath) {
return false
}
if isServerDockerized() {
return false
}
}
return true
}
func getIconPath() string {
return path.Join(config.GetInstance().GetConfigPath(), "icon.png")
}
func RevealInFileManager(path string) {
exists, err := utils.FileExists(path)
if err != nil {
logger.Errorf("Error checking file: %s", err)
return
}
if exists && IsDesktop() {
revealInFileManager(path)
}
}
func getServerURL(path string) string {
c := config.GetInstance()
serverAddress := c.GetHost()
if serverAddress == "0.0.0.0" {
serverAddress = "localhost"
}
serverAddress = serverAddress + ":" + strconv.Itoa(c.GetPort())
proto := ""
if c.HasTLSConfig() {
proto = "https://"
} else {
proto = "http://"
}
serverAddress = proto + serverAddress + "/"
if path != "" {
serverAddress += strings.TrimPrefix(path, "/")
}
return serverAddress
}