From a4cd63f512f31efccf68c34f42aaf4cd2278ba9a Mon Sep 17 00:00:00 2001 From: Alex Ballas Date: Sun, 22 Dec 2024 02:22:53 +0200 Subject: [PATCH] Use standard port for ssdp discovery. Also try to fix CLI issue on windows --- Makefile | 3 +- cmd/go2tv/go2tv.go | 2 +- devices/devices.go | 2 +- go.mod | 12 +++--- go.sum | 24 ++++++------ internal/gui/about.go | 4 +- internal/gui/actions.go | 34 ++++++++--------- internal/gui/gui.go | 33 ++++++++--------- internal/gui/main.go | 12 +++--- internal/gui/newGUI.go | 9 +++++ internal/gui/newGUI_windows.go | 23 ++++++++++++ internal/gui/settings.go | 6 +-- soapcalls/utils/ffprobe_test.go | 55 ++++++++++++++++++++++++++++ soapcalls/utils/substools.go | 4 ++ soapcalls/utils/substools_windows.go | 4 ++ 15 files changed, 160 insertions(+), 67 deletions(-) create mode 100644 internal/gui/newGUI.go create mode 100644 internal/gui/newGUI_windows.go create mode 100644 soapcalls/utils/ffprobe_test.go diff --git a/Makefile b/Makefile index 04b7966..e3a7aee 100644 --- a/Makefile +++ b/Makefile @@ -1,11 +1,10 @@ LDFLAGS="-s -w" -WINLDFLAGS="-H=windowsgui -s -w" build: clean go build -trimpath -ldflags $(LDFLAGS) -o build/go2tv cmd/go2tv/go2tv.go windows: clean - env CGO_ENABLED=1 CC=x86_64-w64-mingw32-gcc CXX=x86_64-w64-mingw32-g++ GOOS=windows GOARCH=amd64 go build -trimpath -ldflags $(WINLDFLAGS) -o build/go2tv.exe cmd/go2tv/go2tv.go + env CGO_ENABLED=1 CC=x86_64-w64-mingw32-gcc CXX=x86_64-w64-mingw32-g++ GOOS=windows GOARCH=amd64 go build -trimpath -ldflags $(LDFLAGS) -o build/go2tv.exe cmd/go2tv/go2tv.go install: build mkdir -vp /usr/local/bin/ diff --git a/cmd/go2tv/go2tv.go b/cmd/go2tv/go2tv.go index 6e96630..25ab73a 100644 --- a/cmd/go2tv/go2tv.go +++ b/cmd/go2tv/go2tv.go @@ -106,7 +106,7 @@ func run() error { } if flagRes.gui { - scr := gui.InitFyneNewScreen(version) + scr := gui.NewFyneScreen(version) gui.Start(exitCTX, scr) return nil } diff --git a/devices/devices.go b/devices/devices.go index cf97633..b8c667f 100644 --- a/devices/devices.go +++ b/devices/devices.go @@ -21,7 +21,7 @@ var ( func LoadSSDPservices(delay int) (map[string]string, error) { // Reset device list every time we call this. urlList := make(map[string]string) - list, err := ssdp.Search(ssdp.All, delay, "") + list, err := ssdp.Search(ssdp.All, delay, "239.255.255.250:1900") if err != nil { return nil, fmt.Errorf("LoadSSDPservices search error: %w", err) } diff --git a/go.mod b/go.mod index 20caaa6..dbaba7b 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/alexballas/go2tv go 1.23.3 require ( - fyne.io/fyne/v2 v2.5.2 + fyne.io/fyne/v2 v2.5.3 github.com/alexballas/go-ssdp v0.0.3 github.com/gdamore/tcell/v2 v2.7.4 github.com/h2non/filetype v1.1.3 @@ -14,6 +14,7 @@ require ( github.com/rs/zerolog v1.33.0 github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 + golang.org/x/sys v0.28.0 golang.org/x/time v0.8.0 ) @@ -30,11 +31,11 @@ require ( github.com/go-gl/gl v0.0.0-20231021071112-07e5d0ea2e71 // indirect github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240506104042-037f3cc74f2a // indirect github.com/go-text/render v0.2.0 // indirect - github.com/go-text/typesetting v0.2.0 // indirect + github.com/go-text/typesetting v0.2.1 // indirect github.com/godbus/dbus/v5 v5.1.0 // indirect github.com/gopherjs/gopherjs v1.17.2 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect - github.com/jeandeaual/go-locale v0.0.0-20241204123234-32dda1c00a20 // indirect + github.com/jeandeaual/go-locale v0.0.0-20241217141322-fcc2cadd6f08 // indirect github.com/jsummers/gobmp v0.0.0-20230614200233-a9de23ed2e25 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect @@ -48,9 +49,8 @@ require ( github.com/stretchr/testify v1.10.0 // indirect github.com/yuin/goldmark v1.7.8 // indirect golang.org/x/image v0.23.0 // indirect - golang.org/x/mobile v0.0.0-20241204233305-ce44b2716d33 // indirect - golang.org/x/net v0.32.0 // indirect - golang.org/x/sys v0.28.0 // indirect + golang.org/x/mobile v0.0.0-20241213221354-a87c1cf6cf46 // indirect + golang.org/x/net v0.33.0 // indirect golang.org/x/term v0.27.0 // indirect golang.org/x/text v0.21.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index d1002fd..b3a3b34 100644 --- a/go.sum +++ b/go.sum @@ -37,8 +37,8 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -fyne.io/fyne/v2 v2.5.2 h1:eSyGTmSkv10yAdAeHpDet6u2KkKxOGFc14kQu81We7Q= -fyne.io/fyne/v2 v2.5.2/go.mod h1:26gqPDvtaxHeyct+C0BBjuGd2zwAJlPkUGSBrb+d7Ug= +fyne.io/fyne/v2 v2.5.3 h1:k6LjZx6EzRZhClsuzy6vucLZBstdH2USDGHSGWq8ly8= +fyne.io/fyne/v2 v2.5.3/go.mod h1:0GOXKqyvNwk3DLmsFu9v0oYM0ZcD1ysGnlHCerKoAmo= fyne.io/systray v1.11.0 h1:D9HISlxSkx+jHSniMBR6fCFOUjk1x/OOOJLa9lJYAKg= fyne.io/systray v1.11.0/go.mod h1:RVwqP9nYMo7h5zViCBHri2FgjXF7H2cub7MAq4NSoLs= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= @@ -107,10 +107,10 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240506104042-037f3cc74f2a h1:vxnBhFDDT+ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240506104042-037f3cc74f2a/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-text/render v0.2.0 h1:LBYoTmp5jYiJ4NPqDc2pz17MLmA3wHw1dZSVGcOdeAc= github.com/go-text/render v0.2.0/go.mod h1:CkiqfukRGKJA5vZZISkjSYrcdtgKQWRa2HIzvwNN5SU= -github.com/go-text/typesetting v0.2.0 h1:fbzsgbmk04KiWtE+c3ZD4W2nmCRzBqrqQOvYlwAOdho= -github.com/go-text/typesetting v0.2.0/go.mod h1:2+owI/sxa73XA581LAzVuEBZ3WEEV2pXeDswCH/3i1I= -github.com/go-text/typesetting-utils v0.0.0-20240317173224-1986cbe96c66 h1:GUrm65PQPlhFSKjLPGOZNPNxLCybjzjYBzjfoBGaDUY= -github.com/go-text/typesetting-utils v0.0.0-20240317173224-1986cbe96c66/go.mod h1:DDxDdQEnB70R8owOx3LVpEFvpMK9eeH1o2r0yZhFI9o= +github.com/go-text/typesetting v0.2.1 h1:x0jMOGyO3d1qFAPI0j4GSsh7M0Q3Ypjzr4+CEVg82V8= +github.com/go-text/typesetting v0.2.1/go.mod h1:mTOxEwasOFpAMBjEQDhdWRckoLLeI/+qrQeBCTGEt6M= +github.com/go-text/typesetting-utils v0.0.0-20241103174707-87a29e9e6066 h1:qCuYC+94v2xrb1PoS4NIDe7DGYtLnU2wWiQe9a1B1c0= +github.com/go-text/typesetting-utils v0.0.0-20241103174707-87a29e9e6066/go.mod h1:DDxDdQEnB70R8owOx3LVpEFvpMK9eeH1o2r0yZhFI9o= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= @@ -216,8 +216,8 @@ github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/J github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/jeandeaual/go-locale v0.0.0-20241204123234-32dda1c00a20 h1:S6sn9jRW3oq1k/Qiwa20F+E57ZVJ2+KB1Vp5xiJ1FlI= -github.com/jeandeaual/go-locale v0.0.0-20241204123234-32dda1c00a20/go.mod h1:pYcXQtDPRThfL37oZEzUVo2Jb8ua0NEC7rZqrxXyuhA= +github.com/jeandeaual/go-locale v0.0.0-20241217141322-fcc2cadd6f08 h1:wMeVzrPO3mfHIWLZtDcSaGAe2I4PW9B/P5nMkRSwCAc= +github.com/jeandeaual/go-locale v0.0.0-20241217141322-fcc2cadd6f08/go.mod h1:ZDXo8KHryOWSIqnsb/CiDq7hQUYryCgdVnxbj8tDG7o= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= @@ -382,8 +382,8 @@ golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPI golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mobile v0.0.0-20211207041440-4e6c2922fdee/go.mod h1:pe2sM7Uk+2Su1y7u/6Z8KJ24D7lepUjFZbhFOrmDfuQ= -golang.org/x/mobile v0.0.0-20241204233305-ce44b2716d33 h1:MeYjBsMjR6w9aXA3oHEJWCQ5bnD5fOjesYMOn52ovQY= -golang.org/x/mobile v0.0.0-20241204233305-ce44b2716d33/go.mod h1:Sf9LBimL0mWKEdgAjRmJ6iu7Z34osHQTK/devqFbM2I= +golang.org/x/mobile v0.0.0-20241213221354-a87c1cf6cf46 h1:E+R1qmJL8cmWTyWXBHVtmqRxr7FdiTwntffsba1F1Tg= +golang.org/x/mobile v0.0.0-20241213221354-a87c1cf6cf46/go.mod h1:Sf9LBimL0mWKEdgAjRmJ6iu7Z34osHQTK/devqFbM2I= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= @@ -434,8 +434,8 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI= -golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs= +golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= +golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= diff --git a/internal/gui/about.go b/internal/gui/about.go index 6f0f8f4..8aac42d 100644 --- a/internal/gui/about.go +++ b/internal/gui/about.go @@ -20,7 +20,7 @@ import ( "fyne.io/fyne/v2/widget" ) -func aboutWindow(s *NewScreen) fyne.CanvasObject { +func aboutWindow(s *FyneScreen) fyne.CanvasObject { sr := fyne.NewStaticResource("Go2TV Icon", go2TVSmallIcon) go2tvImage := canvas.NewImageFromResource(sr) richhead := widget.NewRichTextFromMarkdown(` @@ -70,7 +70,7 @@ MIT return container.NewPadded(cont) } -func checkVersion(s *NewScreen) { +func checkVersion(s *FyneScreen) { s.CheckVersion.Disable() defer s.CheckVersion.Enable() errRedirectChecker := errors.New("redirect") diff --git a/internal/gui/actions.go b/internal/gui/actions.go index d1d93de..810bc8d 100644 --- a/internal/gui/actions.go +++ b/internal/gui/actions.go @@ -30,7 +30,7 @@ import ( "github.com/skratchdot/open-golang/open" ) -func muteAction(screen *NewScreen) { +func muteAction(screen *FyneScreen) { if screen.renderingControlURL == "" { check(screen, errors.New(lang.L("please select a device"))) return @@ -56,7 +56,7 @@ func muteAction(screen *NewScreen) { setMuteUnmuteView("Unmute", screen) } -func unmuteAction(screen *NewScreen) { +func unmuteAction(screen *FyneScreen) { if screen.renderingControlURL == "" { check(screen, errors.New(lang.L("please select a device"))) return @@ -78,7 +78,7 @@ func unmuteAction(screen *NewScreen) { setMuteUnmuteView("Mute", screen) } -func selectMediaFile(screen *NewScreen, f fyne.URI) { +func selectMediaFile(screen *FyneScreen, f fyne.URI) { mfile := f.Path() absMediaFile, err := filepath.Abs(mfile) check(screen, err) @@ -116,7 +116,7 @@ func selectMediaFile(screen *NewScreen, f fyne.URI) { screen.SelectInternalSubs.Enable() } -func selectSubsFile(screen *NewScreen, f fyne.URI) { +func selectSubsFile(screen *FyneScreen, f fyne.URI) { sfile := f.Path() absSubtitlesFile, err := filepath.Abs(sfile) check(screen, err) @@ -131,7 +131,7 @@ func selectSubsFile(screen *NewScreen, f fyne.URI) { screen.SubsText.Refresh() } -func mediaAction(screen *NewScreen) { +func mediaAction(screen *FyneScreen) { w := screen.Current fd := dialog.NewFileOpen(func(reader fyne.URIReadCloser, err error) { check(screen, err) @@ -157,7 +157,7 @@ func mediaAction(screen *NewScreen) { fd.Show() } -func subsAction(screen *NewScreen) { +func subsAction(screen *FyneScreen) { w := screen.Current fd := dialog.NewFileOpen(func(reader fyne.URIReadCloser, err error) { check(screen, err) @@ -185,7 +185,7 @@ func subsAction(screen *NewScreen) { fd.Show() } -func playAction(screen *NewScreen) { +func playAction(screen *FyneScreen) { var mediaFile interface{} screen.PlayPause.Disable() @@ -422,7 +422,7 @@ func playAction(screen *NewScreen) { } -func startAfreshPlayButton(screen *NewScreen) { +func startAfreshPlayButton(screen *FyneScreen) { if screen.cancelEnablePlay != nil { screen.cancelEnablePlay() } @@ -431,7 +431,7 @@ func startAfreshPlayButton(screen *NewScreen) { screen.updateScreenState("Stopped") } -func gaplessMediaWatcher(ctx context.Context, screen *NewScreen, payload *soapcalls.TVPayload) { +func gaplessMediaWatcher(ctx context.Context, screen *FyneScreen, payload *soapcalls.TVPayload) { t := time.NewTicker(1 * time.Second) out: for { @@ -489,12 +489,12 @@ out: } } -func pauseAction(screen *NewScreen) { +func pauseAction(screen *FyneScreen) { err := screen.tvdata.SendtoTV("Pause") check(screen, err) } -func clearmediaAction(screen *NewScreen) { +func clearmediaAction(screen *FyneScreen) { screen.MediaText.Text = "" screen.mediafile = "" screen.MediaText.Refresh() @@ -504,14 +504,14 @@ func clearmediaAction(screen *NewScreen) { screen.SelectInternalSubs.Disable() } -func clearsubsAction(screen *NewScreen) { +func clearsubsAction(screen *FyneScreen) { screen.SelectInternalSubs.ClearSelected() screen.SubsText.Text = "" screen.subsfile = "" screen.SubsText.Refresh() } -func skipNextAction(screen *NewScreen) { +func skipNextAction(screen *FyneScreen) { if screen.controlURL == "" { check(screen, errors.New(lang.L("please select a device"))) return @@ -536,7 +536,7 @@ func skipNextAction(screen *NewScreen) { playAction(screen) } -func previewmedia(screen *NewScreen) { +func previewmedia(screen *FyneScreen) { if screen.mediafile == "" { check(screen, errors.New(lang.L("please select a media file"))) return @@ -571,7 +571,7 @@ func previewmedia(screen *NewScreen) { } } -func stopAction(screen *NewScreen) { +func stopAction(screen *FyneScreen) { screen.PlayPause.Enable() if screen.tvdata == nil || screen.tvdata.ControlURL == "" { @@ -610,7 +610,7 @@ func getDevices(delay int) ([]devType, error) { return guiDeviceList, nil } -func volumeAction(screen *NewScreen, up bool) { +func volumeAction(screen *FyneScreen, up bool) { if screen.renderingControlURL == "" { check(screen, errors.New(lang.L("please select a device"))) return @@ -646,7 +646,7 @@ func volumeAction(screen *NewScreen, up bool) { } } -func queueNext(screen *NewScreen, clear bool) (*soapcalls.TVPayload, error) { +func queueNext(screen *FyneScreen, clear bool) (*soapcalls.TVPayload, error) { if screen.tvdata == nil { return nil, errors.New("queueNext, nil tvdata") } diff --git a/internal/gui/gui.go b/internal/gui/gui.go index 5264573..5c2d8b7 100644 --- a/internal/gui/gui.go +++ b/internal/gui/gui.go @@ -27,8 +27,8 @@ import ( "github.com/alexballas/go2tv/soapcalls/utils" ) -// NewScreen . -type NewScreen struct { +// FyneScreen . +type FyneScreen struct { tempFiles []string SelectInternalSubs *widget.Select CurrentPos binding.String @@ -51,7 +51,7 @@ type NewScreen struct { httpserver *httphandlers.HTTPserver MediaText *widget.Entry ExternalMediaURL *widget.Check - GaplessMediaWatcher func(context.Context, *NewScreen, *soapcalls.TVPayload) + GaplessMediaWatcher func(context.Context, *FyneScreen, *soapcalls.TVPayload) SlideBar *tappedSlider MuteUnmute *widget.Button VolumeDown *widget.Button @@ -102,7 +102,7 @@ func (f *debugWriter) Write(b []byte) (int, error) { var translations embed.FS // Start . -func Start(ctx context.Context, s *NewScreen) { +func Start(ctx context.Context, s *FyneScreen) { if s == nil { return } @@ -174,7 +174,7 @@ func Start(ctx context.Context, s *NewScreen) { } -func onDropFiles(screen *NewScreen) func(p fyne.Position, u []fyne.URI) { +func onDropFiles(screen *FyneScreen) func(p fyne.Position, u []fyne.URI) { return func(p fyne.Position, u []fyne.URI) { var mfiles, sfiles []fyne.URI @@ -205,7 +205,7 @@ func onDropFiles(screen *NewScreen) func(p fyne.Position, u []fyne.URI) { } // EmitMsg Method to implement the screen interface -func (p *NewScreen) EmitMsg(a string) { +func (p *FyneScreen) EmitMsg(a string) { switch a { case "Playing": setPlayPauseView("Pause", p) @@ -225,7 +225,7 @@ func (p *NewScreen) EmitMsg(a string) { // Fini Method to implement the screen interface. // Will only be executed when we receive a callback message, // not when we explicitly click the Stop button. -func (p *NewScreen) Fini() { +func (p *FyneScreen) Fini() { gaplessOption := fyne.CurrentApp().Preferences().StringWithFallback("Gapless", "Disabled") if p.NextMediaCheck.Checked && gaplessOption == "Disabled" { @@ -244,8 +244,7 @@ func (p *NewScreen) Fini() { } } -// InitFyneNewScreen . -func InitFyneNewScreen(version string) *NewScreen { +func InitFyneNewScreen(version string) *FyneScreen { go2tv := app.NewWithID("app.go2tv.go2tv") switch go2tv.Preferences().String("Language") { @@ -268,7 +267,7 @@ func InitFyneNewScreen(version string) *NewScreen { ring: ring.New(1000), } - return &NewScreen{ + return &FyneScreen{ Current: w, currentmfolder: currentDir, mediaFormats: []string{".mp4", ".avi", ".mkv", ".mpeg", ".mov", ".webm", ".m4v", ".mpv", ".dv", ".mp3", ".flac", ".wav", ".m4a", ".jpg", ".jpeg", ".png"}, @@ -277,7 +276,7 @@ func InitFyneNewScreen(version string) *NewScreen { } } -func check(s *NewScreen, err error) { +func check(s *FyneScreen, err error) { s.muError.Lock() defer s.muError.Unlock() @@ -292,7 +291,7 @@ func check(s *NewScreen, err error) { } } -func getNextMedia(screen *NewScreen) (string, string) { +func getNextMedia(screen *FyneScreen) (string, string) { filedir := filepath.Dir(screen.mediafile) filelist, err := os.ReadDir(filedir) check(screen, err) @@ -358,7 +357,7 @@ func getNextMedia(screen *NewScreen) (string, string) { return resName, resPath } -func autoSelectNextSubs(v string, screen *NewScreen) { +func autoSelectNextSubs(v string, screen *FyneScreen) { name, path := getNextPossibleSubs(v) screen.SubsText.Text = name screen.subsfile = path @@ -379,7 +378,7 @@ func getNextPossibleSubs(v string) (string, string) { return name, path } -func setPlayPauseView(s string, screen *NewScreen) { +func setPlayPauseView(s string, screen *FyneScreen) { if screen.cancelEnablePlay != nil { screen.cancelEnablePlay() } @@ -397,7 +396,7 @@ func setPlayPauseView(s string, screen *NewScreen) { } } -func setMuteUnmuteView(s string, screen *NewScreen) { +func setMuteUnmuteView(s string, screen *FyneScreen) { switch s { case "Mute": screen.MuteUnmute.Icon = theme.VolumeUpIcon() @@ -411,14 +410,14 @@ func setMuteUnmuteView(s string, screen *NewScreen) { // updateScreenState updates the screen state based on // the emitted messages. The State variable is used across // the GUI interface to control certain flows. -func (p *NewScreen) updateScreenState(a string) { +func (p *FyneScreen) updateScreenState(a string) { p.mu.Lock() p.State = a p.mu.Unlock() } // getScreenState returns the current screen state -func (p *NewScreen) getScreenState() string { +func (p *FyneScreen) getScreenState() string { p.mu.RLock() defer p.mu.RUnlock() return p.State diff --git a/internal/gui/main.go b/internal/gui/main.go index f0d9306..fffb41a 100644 --- a/internal/gui/main.go +++ b/internal/gui/main.go @@ -28,7 +28,7 @@ import ( type tappedSlider struct { *widget.Slider - screen *NewScreen + screen *FyneScreen end string mu sync.Mutex } @@ -59,7 +59,7 @@ func newDeviceList(dd *[]devType) *deviceList { return list } -func newTappableSlider(s *NewScreen) *tappedSlider { +func newTappableSlider(s *FyneScreen) *tappedSlider { slider := &tappedSlider{ Slider: &widget.Slider{ Max: 100, @@ -207,7 +207,7 @@ func (t *tappedSlider) Tapped(p *fyne.PointEvent) { } } -func mainWindow(s *NewScreen) fyne.CanvasObject { +func mainWindow(s *FyneScreen) fyne.CanvasObject { w := s.Current var data []devType list := newDeviceList(&data) @@ -561,7 +561,7 @@ func mainWindow(s *NewScreen) fyne.CanvasObject { return content } -func refreshDevList(s *NewScreen, data *[]devType) { +func refreshDevList(s *FyneScreen, data *[]devType) { refreshDevices := time.NewTicker(5 * time.Second) _, err := getDevices(2) @@ -625,7 +625,7 @@ func refreshDevList(s *NewScreen, data *[]devType) { } } -func checkMutefunc(s *NewScreen) { +func checkMutefunc(s *FyneScreen) { checkMute := time.NewTicker(2 * time.Second) var checkMuteCounter int @@ -662,7 +662,7 @@ func checkMutefunc(s *NewScreen) { } } -func sliderUpdate(s *NewScreen) { +func sliderUpdate(s *FyneScreen) { t := time.NewTicker(time.Second) for range t.C { if s.sliderActive { diff --git a/internal/gui/newGUI.go b/internal/gui/newGUI.go new file mode 100644 index 0000000..1d7fabf --- /dev/null +++ b/internal/gui/newGUI.go @@ -0,0 +1,9 @@ +//go:build !windows +// +build !windows + +package gui + +// NewFyneScreen . +func NewFyneScreen(version string) *FyneScreen { + return InitFyneNewScreen(version) +} diff --git a/internal/gui/newGUI_windows.go b/internal/gui/newGUI_windows.go new file mode 100644 index 0000000..89f5aaa --- /dev/null +++ b/internal/gui/newGUI_windows.go @@ -0,0 +1,23 @@ +package gui + +import ( + "golang.org/x/sys/windows" +) + +// NewFyneScreen . +func NewFyneScreen(version string) *FyneScreen { + hideConsole() + return InitFyneNewScreen(version) +} + +func hideConsole() { + kernel32 := windows.NewLazySystemDLL("kernel32.dll") + getConsoleWindow := kernel32.NewProc("GetConsoleWindow") + showWindow := windows.NewLazySystemDLL("user32.dll").NewProc("ShowWindow") + + hwnd, _, _ := getConsoleWindow.Call() + if hwnd != 0 { + const SW_HIDE = 0 + showWindow.Call(hwnd, SW_HIDE) + } +} diff --git a/internal/gui/settings.go b/internal/gui/settings.go index 1bd3f9d..f2c9927 100644 --- a/internal/gui/settings.go +++ b/internal/gui/settings.go @@ -16,7 +16,7 @@ import ( "fyne.io/fyne/v2/widget" ) -func settingsWindow(s *NewScreen) fyne.CanvasObject { +func settingsWindow(s *FyneScreen) fyne.CanvasObject { w := s.Current themeText := widget.NewLabel(lang.L("Theme")) @@ -147,7 +147,7 @@ func settingsWindow(s *NewScreen) fyne.CanvasObject { return container.New(layout.NewFormLayout(), themeText, dropdownTheme, languageText, dropdownLanguage, gaplessText, gaplessdropdown, ffmpegText, ffmpegTextEntry, debugText, debugExport) } -func saveDebugLogs(f fyne.URIWriteCloser, s *NewScreen) { +func saveDebugLogs(f fyne.URIWriteCloser, s *FyneScreen) { w := s.Current defer f.Close() @@ -176,7 +176,7 @@ func parseTheme(t string) { }() } -func parseLanguage(s *NewScreen) func(string) { +func parseLanguage(s *FyneScreen) func(string) { w := s.Current return func(t string) { if t != fyne.CurrentApp().Preferences().StringWithFallback("Language", "System Default") { diff --git a/soapcalls/utils/ffprobe_test.go b/soapcalls/utils/ffprobe_test.go new file mode 100644 index 0000000..fd6f282 --- /dev/null +++ b/soapcalls/utils/ffprobe_test.go @@ -0,0 +1,55 @@ +package utils + +import ( + "testing" + "time" +) + +// TestFormatDuration - test formatDuration +func TestFormatDuration(t *testing.T) { + tests := []struct { + name string + input time.Duration + expected string + }{ + { + name: "Zero duration", + input: 0, + expected: "00:00:00.000", + }, + { + name: "Milliseconds only", + input: 123 * time.Millisecond, + expected: "00:00:00.123", + }, + { + name: "Seconds and milliseconds", + input: 12*time.Second + 345*time.Millisecond, + expected: "00:00:12.345", + }, + { + name: "Minutes, seconds, and milliseconds", + input: 5*time.Minute + 23*time.Second + 789*time.Millisecond, + expected: "00:05:23.789", + }, + { + name: "Hours, minutes, seconds, and milliseconds", + input: 2*time.Hour + 15*time.Minute + 9*time.Second + 56*time.Millisecond, + expected: "02:15:09.056", + }, + { + name: "More than a day", + input: 26*time.Hour + 45*time.Minute + 33*time.Second + 1*time.Millisecond, + expected: "26:45:33.001", + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + result := formatDuration(test.input) + if result != test.expected { + t.Fatalf("for input %v, expected %q but got %q", test.input, test.expected, result) + } + }) + } +} diff --git a/soapcalls/utils/substools.go b/soapcalls/utils/substools.go index e82e4ef..b3b266a 100644 --- a/soapcalls/utils/substools.go +++ b/soapcalls/utils/substools.go @@ -29,8 +29,10 @@ type tags struct { Language string `mapstructure:"language"` } +// ErrNoSubs - No subs detected var ErrNoSubs = errors.New("no subs") +// GetSubs - List all subs in our video file. func GetSubs(ffmpeg string, f string) ([]string, error) { _, err := os.Stat(f) if err != nil { @@ -93,6 +95,8 @@ func GetSubs(ffmpeg string, f string) ([]string, error) { return out, nil } +// ExtractSub - Save the extracted sub into a temp file. +// Return the path of that file. func ExtractSub(ffmpeg string, n int, f string) (string, error) { _, err := os.Stat(f) if err != nil { diff --git a/soapcalls/utils/substools_windows.go b/soapcalls/utils/substools_windows.go index c913f1c..c969bb6 100644 --- a/soapcalls/utils/substools_windows.go +++ b/soapcalls/utils/substools_windows.go @@ -27,8 +27,10 @@ type tags struct { Language string `mapstructure:"language"` } +// ErrNoSubs - No subs detected var ErrNoSubs = errors.New("no subs") +// GetSubs - List all subs in our video file. func GetSubs(ffmpeg string, f string) ([]string, error) { _, err := os.Stat(f) if err != nil { @@ -90,6 +92,8 @@ func GetSubs(ffmpeg string, f string) ([]string, error) { return out, nil } +// ExtractSub - Save the extracted sub into a temp file. +// Return the path of that file. func ExtractSub(ffmpeg string, n int, f string) (string, error) { _, err := os.Stat(f) if err != nil {