mirror of https://github.com/stashapp/stash.git
Start browser on server start (#1832)
* Start browser on server start * Add config option for opening browser
This commit is contained in:
parent
29b14ab4fc
commit
87036a07bc
1
go.mod
1
go.mod
|
@ -22,6 +22,7 @@ require (
|
|||
github.com/json-iterator/go v1.1.9
|
||||
github.com/mattn/go-sqlite3 v1.14.6
|
||||
github.com/natefinch/pie v0.0.0-20170715172608-9a0d72014007
|
||||
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8
|
||||
github.com/remeh/sizedwaitgroup v1.0.0
|
||||
github.com/robertkrimen/otto v0.0.0-20200922221731-ef014fd054ac
|
||||
github.com/rs/cors v1.6.0
|
||||
|
|
3
go.sum
3
go.sum
|
@ -554,6 +554,8 @@ github.com/pierrec/lz4/v4 v4.1.4/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuR
|
|||
github.com/pierrec/lz4/v4 v4.1.7/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||
github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA=
|
||||
github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4/go.mod h1:N6UoU20jOqggOuDwUaBQpluzLNDqif3kq9z2wpdYEfQ=
|
||||
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU=
|
||||
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
|
@ -901,6 +903,7 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20210521090106-6ca3eb03dfc2/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
|
|
|
@ -52,6 +52,7 @@ fragment ConfigInterfaceData on ConfigInterfaceResult {
|
|||
wallShowTitle
|
||||
wallPlayback
|
||||
maximumLoopDuration
|
||||
noBrowser
|
||||
autostartVideo
|
||||
showStudioAsText
|
||||
css
|
||||
|
|
|
@ -222,6 +222,8 @@ input ConfigInterfaceInput {
|
|||
handyKey: String
|
||||
"""Funscript Time Offset"""
|
||||
funscriptOffset: Int
|
||||
"""True if we should not auto-open a browser window on startup"""
|
||||
noBrowser: Boolean
|
||||
}
|
||||
|
||||
type ConfigDisableDropdownCreate {
|
||||
|
@ -241,6 +243,8 @@ type ConfigInterfaceResult {
|
|||
wallPlayback: String
|
||||
"""Maximum duration (in seconds) in which a scene video will loop in the scene player"""
|
||||
maximumLoopDuration: Int
|
||||
""""True if we should not auto-open a browser window on startup"""
|
||||
noBrowser: Boolean
|
||||
"""If true, video will autostart on load in the scene player"""
|
||||
autostartVideo: Boolean
|
||||
"""If true, studio overlays will be shown as text instead of logo images"""
|
||||
|
|
|
@ -239,6 +239,8 @@ func (r *mutationResolver) ConfigureInterface(ctx context.Context, input models.
|
|||
setBool(config.SoundOnPreview, input.SoundOnPreview)
|
||||
setBool(config.WallShowTitle, input.WallShowTitle)
|
||||
|
||||
setBool(config.NoBrowser, input.NoBrowser)
|
||||
|
||||
if input.WallPlayback != nil {
|
||||
c.Set(config.WallPlayback, *input.WallPlayback)
|
||||
}
|
||||
|
|
|
@ -108,6 +108,7 @@ func makeConfigInterfaceResult() *models.ConfigInterfaceResult {
|
|||
soundOnPreview := config.GetSoundOnPreview()
|
||||
wallShowTitle := config.GetWallShowTitle()
|
||||
wallPlayback := config.GetWallPlayback()
|
||||
noBrowser := config.GetNoBrowserFlag()
|
||||
maximumLoopDuration := config.GetMaximumLoopDuration()
|
||||
autostartVideo := config.GetAutostartVideo()
|
||||
showStudioAsText := config.GetShowStudioAsText()
|
||||
|
@ -124,6 +125,7 @@ func makeConfigInterfaceResult() *models.ConfigInterfaceResult {
|
|||
WallShowTitle: &wallShowTitle,
|
||||
WallPlayback: &wallPlayback,
|
||||
MaximumLoopDuration: &maximumLoopDuration,
|
||||
NoBrowser: &noBrowser,
|
||||
AutostartVideo: &autostartVideo,
|
||||
ShowStudioAsText: &showStudioAsText,
|
||||
CSS: &css,
|
||||
|
|
|
@ -23,6 +23,7 @@ import (
|
|||
"github.com/go-chi/chi"
|
||||
"github.com/go-chi/chi/middleware"
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/pkg/browser"
|
||||
"github.com/rs/cors"
|
||||
"github.com/stashapp/stash/pkg/logger"
|
||||
"github.com/stashapp/stash/pkg/manager"
|
||||
|
@ -243,12 +244,26 @@ func Start(uiBox embed.FS, loginUIBox embed.FS) {
|
|||
printVersion()
|
||||
printLatestVersion(context.TODO())
|
||||
logger.Infof("stash is listening on " + address)
|
||||
if tlsConfig != nil {
|
||||
displayAddress = "https://" + displayAddress + "/"
|
||||
} else {
|
||||
displayAddress = "http://" + displayAddress + "/"
|
||||
}
|
||||
|
||||
// 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.
|
||||
if !c.GetNoBrowserFlag() && manager.GetInstance().IsDesktop() {
|
||||
err = browser.OpenURL(displayAddress)
|
||||
if err != nil {
|
||||
logger.Error("Could not open browser: " + err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
if tlsConfig != nil {
|
||||
logger.Infof("stash is running at https://" + displayAddress + "/")
|
||||
logger.Infof("stash is running at " + displayAddress)
|
||||
logger.Error(server.ListenAndServeTLS("", ""))
|
||||
} else {
|
||||
logger.Infof("stash is running at http://" + displayAddress + "/")
|
||||
logger.Infof("stash is running at " + displayAddress)
|
||||
logger.Error(server.ListenAndServe())
|
||||
}
|
||||
}()
|
||||
|
|
|
@ -165,6 +165,10 @@ const DLNADefaultEnabled = "dlna.default_enabled"
|
|||
const DLNADefaultIPWhitelist = "dlna.default_whitelist"
|
||||
const DLNAInterfaces = "dlna.interfaces"
|
||||
|
||||
// Desktop Integration Options
|
||||
const NoBrowser = "noBrowser"
|
||||
const NoBrowserDefault = false
|
||||
|
||||
// Logging options
|
||||
const LogFile = "logFile"
|
||||
const LogOut = "logOut"
|
||||
|
@ -247,6 +251,12 @@ func (i *Instance) GetCPUProfilePath() string {
|
|||
return i.cpuProfilePath
|
||||
}
|
||||
|
||||
func (i *Instance) GetNoBrowserFlag() bool {
|
||||
i.Lock()
|
||||
defer i.Unlock()
|
||||
return viper.GetBool(NoBrowser)
|
||||
}
|
||||
|
||||
func (i *Instance) Set(key string, value interface{}) {
|
||||
i.Lock()
|
||||
defer i.Unlock()
|
||||
|
@ -1098,6 +1108,8 @@ func (i *Instance) setDefaultValues(write bool) error {
|
|||
// Set generated to the metadata path for backwards compat
|
||||
viper.SetDefault(Generated, viper.GetString(Metadata))
|
||||
|
||||
viper.SetDefault(NoBrowser, NoBrowserDefault)
|
||||
|
||||
// Set default scrapers and plugins paths
|
||||
viper.SetDefault(ScrapersPath, defaultScrapersPath)
|
||||
viper.SetDefault(PluginsPath, defaultPluginsPath)
|
||||
|
|
|
@ -19,6 +19,7 @@ var once sync.Once
|
|||
type flagStruct struct {
|
||||
configFilePath string
|
||||
cpuProfilePath string
|
||||
nobrowser bool
|
||||
}
|
||||
|
||||
func Initialize() (*Instance, error) {
|
||||
|
@ -42,6 +43,7 @@ func Initialize() (*Instance, error) {
|
|||
}
|
||||
|
||||
if !instance.isNewSystem {
|
||||
setExistingSystemDefaults(instance)
|
||||
err = instance.SetInitialConfig()
|
||||
}
|
||||
})
|
||||
|
@ -97,6 +99,28 @@ func initConfig(flags flagStruct) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// setExistingSystemDefaults sets config options that are new and unset in an existing install,
|
||||
// but should have a separate default than for brand-new systems, to maintain behavior.
|
||||
func setExistingSystemDefaults(instance *Instance) {
|
||||
if !instance.isNewSystem {
|
||||
configDirtied := false
|
||||
|
||||
// Existing systems as of the introduction of auto-browser open should retain existing
|
||||
// behavior and not start the browser automatically.
|
||||
if !viper.InConfig("nobrowser") {
|
||||
configDirtied = true
|
||||
viper.Set("nobrowser", "true")
|
||||
}
|
||||
|
||||
if configDirtied {
|
||||
err := viper.WriteConfig()
|
||||
if err != nil {
|
||||
logger.Errorf("Could not save existing system defaults: %s", err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func initFlags() flagStruct {
|
||||
flags := flagStruct{}
|
||||
|
||||
|
@ -104,6 +128,7 @@ func initFlags() flagStruct {
|
|||
pflag.Int("port", 9999, "port to serve from")
|
||||
pflag.StringVarP(&flags.configFilePath, "config", "c", "", "config file to use")
|
||||
pflag.StringVar(&flags.cpuProfilePath, "cpuprofile", "", "write cpu profile to file")
|
||||
pflag.BoolVar(&flags.nobrowser, "nobrowser", false, "Don't open a browser window after launch")
|
||||
|
||||
pflag.Parse()
|
||||
if err := viper.BindPFlags(pflag.CommandLine); err != nil {
|
||||
|
|
|
@ -4,9 +4,12 @@ import (
|
|||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"runtime/pprof"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
|
@ -391,6 +394,34 @@ func (s *singleton) Migrate(ctx context.Context, input models.MigrateInput) erro
|
|||
return nil
|
||||
}
|
||||
|
||||
func (s *singleton) IsDesktop() bool {
|
||||
// check if running under root
|
||||
if os.Getuid() == 0 {
|
||||
return false
|
||||
}
|
||||
// check if started by init, e.g. stash is a *nix systemd service / MacOS launchd service
|
||||
if os.Getppid() == 1 {
|
||||
return false
|
||||
}
|
||||
if IsServerDockerized() {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func IsServerDockerized() bool {
|
||||
if runtime.GOOS == "linux" {
|
||||
_, dockerEnvErr := os.Stat("/.dockerenv")
|
||||
cgroups, _ := ioutil.ReadFile("/proc/self/cgroup")
|
||||
if os.IsExist(dockerEnvErr) || strings.Contains(string(cgroups), "docker") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (s *singleton) GetSystemStatus() *models.SystemStatus {
|
||||
status := models.SystemStatusEnumOk
|
||||
dbSchema := int(database.Version())
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
### ✨ New Features
|
||||
* Optionally open browser on startup (enabled by default for new systems). ([#1832](https://github.com/stashapp/stash/pull/1832))
|
||||
* Support setting defaults for Delete File and Delete Generated Files in the Interface Settings. ([#1852](https://github.com/stashapp/stash/pull/1852))
|
||||
* Added Identify task to automatically identify scenes from stash-box/scraper sources. See manual entry for details. ([#1839](https://github.com/stashapp/stash/pull/1839))
|
||||
* Added support for matching scenes using perceptual hashes when querying stash-box. ([#1858](https://github.com/stashapp/stash/pull/1858))
|
||||
|
|
|
@ -32,6 +32,7 @@ export const SettingsInterfacePanel: React.FC = () => {
|
|||
const [menuItemIds, setMenuItemIds] = useState<string[]>(
|
||||
allMenuItems.map((item) => item.id)
|
||||
);
|
||||
const [noBrowser, setNoBrowserFlag] = useState<boolean>(false);
|
||||
const [soundOnPreview, setSoundOnPreview] = useState<boolean>(true);
|
||||
const [wallShowTitle, setWallShowTitle] = useState<boolean>(true);
|
||||
const [wallPlayback, setWallPlayback] = useState<string>("video");
|
||||
|
@ -59,6 +60,7 @@ export const SettingsInterfacePanel: React.FC = () => {
|
|||
wallShowTitle,
|
||||
wallPlayback,
|
||||
maximumLoopDuration,
|
||||
noBrowser,
|
||||
autostartVideo,
|
||||
showStudioAsText,
|
||||
css,
|
||||
|
@ -80,6 +82,7 @@ export const SettingsInterfacePanel: React.FC = () => {
|
|||
setWallShowTitle(iCfg.wallShowTitle ?? true);
|
||||
setWallPlayback(iCfg.wallPlayback ?? "video");
|
||||
setMaximumLoopDuration(iCfg.maximumLoopDuration ?? 0);
|
||||
setNoBrowserFlag(iCfg?.noBrowser ?? false);
|
||||
setAutostartVideo(iCfg.autostartVideo ?? false);
|
||||
setShowStudioAsText(iCfg.showStudioAsText ?? false);
|
||||
setCSS(iCfg.css ?? "");
|
||||
|
@ -180,6 +183,30 @@ export const SettingsInterfacePanel: React.FC = () => {
|
|||
</Form.Text>
|
||||
</Form.Group>
|
||||
|
||||
<hr />
|
||||
|
||||
<h4>
|
||||
{intl.formatMessage({
|
||||
id: "config.ui.desktop_integration.desktop_integration",
|
||||
})}
|
||||
</h4>
|
||||
<Form.Group>
|
||||
<Form.Check
|
||||
id="skip-browser"
|
||||
checked={noBrowser}
|
||||
label={intl.formatMessage({
|
||||
id: "config.ui.desktop_integration.skip_opening_browser",
|
||||
})}
|
||||
onChange={() => setNoBrowserFlag(!noBrowser)}
|
||||
/>
|
||||
<Form.Text className="text-muted">
|
||||
{intl.formatMessage({
|
||||
id: "config.ui.desktop_integration.skip_opening_browser_on_startup",
|
||||
})}
|
||||
</Form.Text>
|
||||
</Form.Group>
|
||||
<hr />
|
||||
|
||||
<Form.Group>
|
||||
<h5>{intl.formatMessage({ id: "config.ui.scene_wall.heading" })}</h5>
|
||||
<Form.Check
|
||||
|
|
|
@ -374,6 +374,11 @@
|
|||
"delete_generated_supporting_files": "Delete generated supporting files by default"
|
||||
}
|
||||
},
|
||||
"desktop_integration": {
|
||||
"desktop_integration": "Desktop Integration",
|
||||
"skip_opening_browser": "Skip Opening Browser",
|
||||
"skip_opening_browser_on_startup": "Skip auto-opening browser during startup"
|
||||
},
|
||||
"editing": {
|
||||
"disable_dropdown_create": {
|
||||
"heading": "Disable dropdown create",
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
Copyright (c) 2014, Dave Cheney <dave@cheney.net>
|
||||
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.
|
||||
|
||||
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 HOLDER 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.
|
|
@ -0,0 +1,55 @@
|
|||
|
||||
# browser
|
||||
import "github.com/pkg/browser"
|
||||
|
||||
Package browser provides helpers to open files, readers, and urls in a browser window.
|
||||
|
||||
The choice of which browser is started is entirely client dependant.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Variables
|
||||
``` go
|
||||
var Stderr io.Writer = os.Stderr
|
||||
```
|
||||
Stderr is the io.Writer to which executed commands write standard error.
|
||||
|
||||
``` go
|
||||
var Stdout io.Writer = os.Stdout
|
||||
```
|
||||
Stdout is the io.Writer to which executed commands write standard output.
|
||||
|
||||
|
||||
## func OpenFile
|
||||
``` go
|
||||
func OpenFile(path string) error
|
||||
```
|
||||
OpenFile opens new browser window for the file path.
|
||||
|
||||
|
||||
## func OpenReader
|
||||
``` go
|
||||
func OpenReader(r io.Reader) error
|
||||
```
|
||||
OpenReader consumes the contents of r and presents the
|
||||
results in a new browser window.
|
||||
|
||||
|
||||
## func OpenURL
|
||||
``` go
|
||||
func OpenURL(url string) error
|
||||
```
|
||||
OpenURL opens a new browser window pointing to url.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
- - -
|
||||
Generated by [godoc2md](http://godoc.org/github.com/davecheney/godoc2md)
|
|
@ -0,0 +1,57 @@
|
|||
// Package browser provides helpers to open files, readers, and urls in a browser window.
|
||||
//
|
||||
// The choice of which browser is started is entirely client dependant.
|
||||
package browser
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// Stdout is the io.Writer to which executed commands write standard output.
|
||||
var Stdout io.Writer = os.Stdout
|
||||
|
||||
// Stderr is the io.Writer to which executed commands write standard error.
|
||||
var Stderr io.Writer = os.Stderr
|
||||
|
||||
// OpenFile opens new browser window for the file path.
|
||||
func OpenFile(path string) error {
|
||||
path, err := filepath.Abs(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return OpenURL("file://" + path)
|
||||
}
|
||||
|
||||
// OpenReader consumes the contents of r and presents the
|
||||
// results in a new browser window.
|
||||
func OpenReader(r io.Reader) error {
|
||||
f, err := ioutil.TempFile("", "browser.*.html")
|
||||
if err != nil {
|
||||
return fmt.Errorf("browser: could not create temporary file: %v", err)
|
||||
}
|
||||
if _, err := io.Copy(f, r); err != nil {
|
||||
f.Close()
|
||||
return fmt.Errorf("browser: caching temporary file failed: %v", err)
|
||||
}
|
||||
if err := f.Close(); err != nil {
|
||||
return fmt.Errorf("browser: caching temporary file failed: %v", err)
|
||||
}
|
||||
return OpenFile(f.Name())
|
||||
}
|
||||
|
||||
// OpenURL opens a new browser window pointing to url.
|
||||
func OpenURL(url string) error {
|
||||
return openBrowser(url)
|
||||
}
|
||||
|
||||
func runCmd(prog string, args ...string) error {
|
||||
cmd := exec.Command(prog, args...)
|
||||
cmd.Stdout = Stdout
|
||||
cmd.Stderr = Stderr
|
||||
return cmd.Run()
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
package browser
|
||||
|
||||
func openBrowser(url string) error {
|
||||
return runCmd("open", url)
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package browser
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
func openBrowser(url string) error {
|
||||
err := runCmd("xdg-open", url)
|
||||
if e, ok := err.(*exec.Error); ok && e.Err == exec.ErrNotFound {
|
||||
return errors.New("xdg-open: command not found - install xdg-utils from ports(8)")
|
||||
}
|
||||
return err
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
package browser
|
||||
|
||||
import (
|
||||
"os/exec"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func openBrowser(url string) error {
|
||||
providers := []string{"xdg-open", "x-www-browser", "www-browser"}
|
||||
|
||||
// There are multiple possible providers to open a browser on linux
|
||||
// One of them is xdg-open, another is x-www-browser, then there's www-browser, etc.
|
||||
// Look for one that exists and run it
|
||||
for _, provider := range providers {
|
||||
if _, err := exec.LookPath(provider); err == nil {
|
||||
return runCmd(provider, url)
|
||||
}
|
||||
}
|
||||
|
||||
return &exec.Error{Name: strings.Join(providers, ","), Err: exec.ErrNotFound}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package browser
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
func openBrowser(url string) error {
|
||||
err := runCmd("xdg-open", url)
|
||||
if e, ok := err.(*exec.Error); ok && e.Err == exec.ErrNotFound {
|
||||
return errors.New("xdg-open: command not found - install xdg-utils from pkgsrc(7)")
|
||||
}
|
||||
return err
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package browser
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
func openBrowser(url string) error {
|
||||
err := runCmd("xdg-open", url)
|
||||
if e, ok := err.(*exec.Error); ok && e.Err == exec.ErrNotFound {
|
||||
return errors.New("xdg-open: command not found - install xdg-utils from ports(8)")
|
||||
}
|
||||
return err
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
// +build !linux,!windows,!darwin,!openbsd,!freebsd,!netbsd
|
||||
|
||||
package browser
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
func openBrowser(url string) error {
|
||||
return fmt.Errorf("openBrowser: unsupported operating system: %v", runtime.GOOS)
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
package browser
|
||||
|
||||
import "golang.org/x/sys/windows"
|
||||
|
||||
func openBrowser(url string) error {
|
||||
return windows.ShellExecute(0, nil, windows.StringToUTF16Ptr(url), nil, nil, windows.SW_SHOWNORMAL)
|
||||
}
|
|
@ -249,6 +249,9 @@ github.com/nfnt/resize
|
|||
# github.com/pelletier/go-toml v1.7.0
|
||||
## explicit; go 1.12
|
||||
github.com/pelletier/go-toml
|
||||
# github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8
|
||||
## explicit; go 1.14
|
||||
github.com/pkg/browser
|
||||
# github.com/pkg/errors v0.9.1
|
||||
## explicit
|
||||
github.com/pkg/errors
|
||||
|
|
Loading…
Reference in New Issue