From 927467060b211435f4120a996a03b827438fd1b9 Mon Sep 17 00:00:00 2001 From: lostdusty <47502554+lostdusty@users.noreply.github.com> Date: Thu, 4 Jul 2024 12:56:00 -0300 Subject: [PATCH] v1.0.3: many new things --- FyneApp.toml | 4 +- go.mod | 18 +-- go.sum | 18 +++ main.go | 339 ++++++++++++++++++++++++++++++++---------------- theme_cobalt.go | 90 +++++++++++++ 5 files changed, 344 insertions(+), 125 deletions(-) create mode 100644 theme_cobalt.go diff --git a/FyneApp.toml b/FyneApp.toml index c0e3b23..604fd8c 100644 --- a/FyneApp.toml +++ b/FyneApp.toml @@ -2,5 +2,5 @@ Icon = "icon.png" Name = "Gualto" ID = "com.lostdusty.gualto" - Version = "1.0.2" - Build = 15 + Version = "1.0.3" + Build = 23 diff --git a/go.mod b/go.mod index 5266797..0e624a6 100644 --- a/go.mod +++ b/go.mod @@ -4,11 +4,11 @@ go 1.21.5 require ( fyne.io/fyne/v2 v2.4.5 - github.com/lostdusty/gobalt v1.0.6 + github.com/lostdusty/gobalt v1.0.8 ) require ( - fyne.io/systray v1.10.1-0.20231230205326-d160fd363db9 // indirect + fyne.io/systray v1.11.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/fredbi/uri v1.1.0 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect @@ -16,7 +16,7 @@ require ( github.com/fyne-io/glfw-js v0.0.0-20240101223322-6e1efdc71b7a // indirect github.com/fyne-io/image v0.0.0-20240417123036-dc0ee9e7c964 // indirect github.com/go-gl/gl v0.0.0-20231021071112-07e5d0ea2e71 // indirect - github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240307211618-a69d953ea142 // indirect + github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240506104042-037f3cc74f2a // indirect github.com/go-text/render v0.1.0 // indirect github.com/go-text/typesetting v0.1.1 // indirect github.com/godbus/dbus/v5 v5.1.0 // indirect @@ -26,12 +26,12 @@ require ( github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c // indirect github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef // indirect github.com/stretchr/testify v1.9.0 // indirect - github.com/yuin/goldmark v1.7.1 // indirect - golang.org/x/image v0.15.0 // indirect - golang.org/x/mobile v0.0.0-20240404231514-09dbf07665ed // indirect - golang.org/x/net v0.24.0 // indirect - golang.org/x/sys v0.19.0 // indirect - golang.org/x/text v0.14.0 // indirect + github.com/yuin/goldmark v1.7.4 // indirect + golang.org/x/image v0.18.0 // indirect + golang.org/x/mobile v0.0.0-20240604190613-2782386b8afd // indirect + golang.org/x/net v0.26.0 // indirect + golang.org/x/sys v0.21.0 // indirect + golang.org/x/text v0.16.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect honnef.co/go/js/dom v0.0.0-20231112215516-51f43a291193 // indirect ) diff --git a/go.sum b/go.sum index 107d99a..29efabe 100644 --- a/go.sum +++ b/go.sum @@ -41,6 +41,8 @@ fyne.io/fyne/v2 v2.4.5 h1:W6jpAEmLoBbKyBB+EXqI7GMJ7kLgHQWCa0wZHUV2VfQ= fyne.io/fyne/v2 v2.4.5/go.mod h1:SlOgbca0y80cRObu/JOhxIJdIgtoW7aCyqUVlTMgs0Y= fyne.io/systray v1.10.1-0.20231230205326-d160fd363db9 h1:E/gHmMVyk8TuI6JIgNIv/Qu1JABMVFBIkQ8lYRa5gkQ= fyne.io/systray v1.10.1-0.20231230205326-d160fd363db9/go.mod h1:RVwqP9nYMo7h5zViCBHri2FgjXF7H2cub7MAq4NSoLs= +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= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= @@ -93,6 +95,8 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2 github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240307211618-a69d953ea142 h1:/4YI5K2b16JtP2cL4D2xDNvH/ESm2ZbGJ0VsudkHJ5s= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240307211618-a69d953ea142/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240506104042-037f3cc74f2a h1:vxnBhFDDT+xzxf1jTJKMKZw3H0swfWk9RpWbBbDK5+0= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240506104042-037f3cc74f2a/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-text/render v0.1.0 h1:osrmVDZNHuP1RSu3pNG7Z77Sd2xSbcb/xWytAj9kyVs= github.com/go-text/render v0.1.0/go.mod h1:jqEuNMenrmj6QRnkdpeaP0oKGFLDNhDkVKwGjsWWYU4= github.com/go-text/typesetting v0.1.1 h1:bGAesCuo85nXnEN5LmFMVGAGpGkCPtHrZLi//qD7EJo= @@ -212,6 +216,8 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/lostdusty/gobalt v1.0.6 h1:vCVAlZS7dyc8x3Xdt4WmMeO9v2wriSIXwaK+binNz2U= github.com/lostdusty/gobalt v1.0.6/go.mod h1:gv+Hmbv0SC3lWpJvUvxJYRmmcuBjlmhKCIRskk0lSjY= +github.com/lostdusty/gobalt v1.0.8 h1:gzYYf20u5OO5IErcNsC68m6xx8i9upX0lEat4MXl2jw= +github.com/lostdusty/gobalt v1.0.8/go.mod h1:gv+Hmbv0SC3lWpJvUvxJYRmmcuBjlmhKCIRskk0lSjY= github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= @@ -278,6 +284,8 @@ github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1 github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.7.1 h1:3bajkSilaCbjdKVsKdZjZCLBNPL9pYzrCakKaf4U49U= github.com/yuin/goldmark v1.7.1/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E= +github.com/yuin/goldmark v1.7.4 h1:BDXOHExt+A7gwPCJgPIIq7ENvceR7we7rOS9TNoLZeg= +github.com/yuin/goldmark v1.7.4/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E= go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= @@ -314,6 +322,8 @@ golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMx golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.15.0 h1:kOELfmgrmJlw4Cdb7g/QGuB3CvDrXbqEIww/pNtNBm8= golang.org/x/image v0.15.0/go.mod h1:HUYqC05R2ZcZ3ejNQsIHQDQiwWM4JBqmm6MKANTp4LE= +golang.org/x/image v0.18.0 h1:jGzIakQa/ZXI1I0Fxvaa9W7yP25TqT6cHIHn+6CqvSQ= +golang.org/x/image v0.18.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -331,6 +341,8 @@ golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCc golang.org/x/mobile v0.0.0-20211207041440-4e6c2922fdee/go.mod h1:pe2sM7Uk+2Su1y7u/6Z8KJ24D7lepUjFZbhFOrmDfuQ= golang.org/x/mobile v0.0.0-20240404231514-09dbf07665ed h1:vZhAhVr5zF1IJaVKTawyTq78WSspLnK53iuMJ1fJgLc= golang.org/x/mobile v0.0.0-20240404231514-09dbf07665ed/go.mod h1:z041I2NhLjANgIfD0XbB2AmUZ8sLUcSgyLaSNGEP50M= +golang.org/x/mobile v0.0.0-20240604190613-2782386b8afd h1:ow0zRCrn9LoaazcXsRUYYjFp+cwkdgB/vlW/ZJEzNRw= +golang.org/x/mobile v0.0.0-20240604190613-2782386b8afd/go.mod h1:CKRkjuY6XI4LTd47GM+4lCT8aFnTO+FcLAGMu0ibd0g= 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= @@ -379,6 +391,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.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= +golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= +golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= 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= @@ -449,6 +463,8 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= +golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -460,6 +476,8 @@ golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= diff --git a/main.go b/main.go index 4291d37..3aaaded 100644 --- a/main.go +++ b/main.go @@ -14,36 +14,56 @@ import ( "fyne.io/fyne/v2/container" "fyne.io/fyne/v2/data/validation" "fyne.io/fyne/v2/dialog" + "fyne.io/fyne/v2/storage" "fyne.io/fyne/v2/theme" "fyne.io/fyne/v2/widget" "github.com/lostdusty/gobalt" ) -var verifyLink = regexp.MustCompile(`[-a-zA-Z0-9@:%_\+.~#?&//=]{2,256}\.[a-z]{2,4}\b(\/[-a-zA-Z0-9@:%_\+.~#?&//=]*)?`) -var blockClip bool -var GualtoWin fyne.Window -var instancesList []string +var ( + verifyLink = regexp.MustCompile(`[-a-zA-Z0-9@:%_\+.~#?&//=]{2,256}\.[a-z]{2,4}\b(\/[-a-zA-Z0-9@:%_\+.~#?&//=]*)?`) + blockClip bool + GualtoWin fyne.Window + GualtoApp fyne.App + cobaltInstances []string +) + +func discoverCobaltInstances() { + asyncGetCobaltInstances, err := gobalt.GetCobaltInstances() + if err != nil { + dialog.ShowError(fmt.Errorf("failed to fetch more cobalt instances"), GualtoWin) + cobaltInstances = append(cobaltInstances, gobalt.CobaltApi) + return + } + + for _, dcobaltInstances := range asyncGetCobaltInstances { + cobaltInstances = append(cobaltInstances, fmt.Sprintf("https://%v", dcobaltInstances.URL)) + } + +} func main() { newDownload := gobalt.CreateDefaultSettings() //Create default settings for downloading - gualtoApp := app.NewWithID("com.lostdusty.gualto") - GualtoWin = gualtoApp.NewWindow("Gualto") + GualtoApp = app.NewWithID("com.lostdusty.gualto") + GualtoWin = GualtoApp.NewWindow("Gualto") GualtoWin.CenterOnScreen() - GualtoWin.Resize(fyne.Size{Width: 800, Height: 400}) + GualtoWin.Resize(fyne.Size{Width: 600, Height: 400}) - //Async fetches cobalt instances. If it fails, add only the main instance to the list - go func() { - asyncGetCobaltInstances, err := gobalt.GetCobaltInstances() - if err != nil { - dialog.ShowError(fmt.Errorf("failed to fetch more cobalt instances"), GualtoWin) - instancesList = append(instancesList, gobalt.CobaltApi) - return - } + /* APP SETTINGS GETTERS + */ + storedInstance := GualtoApp.Preferences().StringWithFallback("instance", gobalt.CobaltApi) + checkClipboard := GualtoApp.Preferences().BoolWithFallback("clipboard", true) + shouldRememberPath := GualtoApp.Preferences().BoolWithFallback("remember-path", true) + GualtoApp.Preferences().StringWithFallback("path", "") + newTheme := GualtoApp.Preferences().BoolWithFallback("theme", false) + if newTheme { + GualtoApp.Settings().SetTheme(cobaltTheme{}) + } + /* END OF THE APP SETTINGS SECTION + */ - for _, cobaltInstances := range asyncGetCobaltInstances { - instancesList = append(instancesList, fmt.Sprintf("https://%v", cobaltInstances.URL)) - } - }() + //Async fetches cobalt instances. If it fails, add only the main instance to the list + go discoverCobaltInstances() labelMain := widget.NewRichTextFromMarkdown("# Gualto\n\nSave what you love, no extra bullshit. Paste your url below to begin the download.") labelMain.Wrapping = fyne.TextWrapWord @@ -62,20 +82,60 @@ func main() { } }) - checkAccordionSettingTwitter := widget.NewCheck("Don't convert Twitter gifs", func(b bool) { - newDownload.ConvertTwitterGifs = b + /* Author: LD + PART I: Ui components for the download settings related children. + + SECTION I: GENERAL OPTIONS + MODIFIED: 03/07/2024 + */ + downloadSettingLabelGeneral := widget.NewRichTextFromMarkdown("### General") + // "General" + + downloadSettingDisableMetadata := widget.NewCheck("Disable metadata?", func(b bool) { + newDownload.DisableVideoMetadata = b + }) + //[] Disable metadata? + + downloadSettingLabelFilenamePattern := widget.NewLabel("Name file as:") + downloadSettingSelectFilenamePattern := widget.NewSelect([]string{"classic", "basic", "pretty", "nerdy"}, func(s string) { + switch s { + case "basic": + newDownload.FilenamePattern = gobalt.Basic + case "classic": + newDownload.FilenamePattern = gobalt.Classic + case "nerdy": + newDownload.FilenamePattern = gobalt.Nerdy + case "pretty": + newDownload.FilenamePattern = gobalt.Pretty + } }) - checkAccordionSettingTwitter.Checked = true + downloadSettingSelectFilenamePattern.SetSelectedIndex(0) + //Name file as: [Basic] + + boxFilenameSettings := container.NewHBox(downloadSettingLabelFilenamePattern, downloadSettingSelectFilenamePattern) + // Container to group checkbox & text to make: [] Disable metadata? + + groupGeneralDownload := container.NewVBox(downloadSettingLabelGeneral, downloadSettingDisableMetadata, boxFilenameSettings) + //Merge them all like: + // ## General + // [] Disable metadata? + // Name file as: [Basic] + + /* SECTION II: VIDEO SETTINGS */ + + downloadSettingLabelVideo := widget.NewRichTextFromMarkdown("### Video") + // ## Video - labelAccordionSettingQuality := widget.NewLabel("Video Quality") - selAccordionSettingQuality := widget.NewSelect([]string{"360", "480", "720", "1080", "1440", "2160"}, func(s string) { + downloadSettingLabelQuality := widget.NewLabel("Video Quality:") + downloadSettingSelectQuality := widget.NewSelect([]string{"144", "240", "360", "480", "720", "1080", "1440", "2160"}, func(s string) { newDownload.VideoQuality, _ = strconv.Atoi(s) }) - selAccordionSettingQuality.Selected = "1080" - qualitySettings := container.NewHBox(labelAccordionSettingQuality, selAccordionSettingQuality) + downloadSettingSelectQuality.Selected = "1080" + boxQualitySettings := container.NewHBox(downloadSettingLabelQuality, downloadSettingSelectQuality) + // Video Quality: [1080] - labelAccordionSettingCodec := widget.NewLabel("Video Codec") - selAccordionSettingCodec := widget.NewSelect([]string{"h264", "av1", "vp9"}, func(s string) { + downloadSettingLabelVideoCodec := widget.NewLabel("Youtube Video Codec:") + downloadSettingSelectVideoCodec := widget.NewSelect([]string{"h264", "av1", "vp9"}, func(s string) { //TODO: Move this to gobalt switch s { case "h264": @@ -86,11 +146,28 @@ func main() { newDownload.VideoCodec = gobalt.VP9 } }) - selAccordionSettingCodec.Selected = "h264" - codecSettings := container.NewHBox(labelAccordionSettingCodec, selAccordionSettingCodec) + downloadSettingSelectVideoCodec.Selected = "h264" + boxCodecSettings := container.NewHBox(downloadSettingLabelVideoCodec, downloadSettingSelectVideoCodec) + // Youtube Video Codec: [h264] + + downloadSettingRemoveVideo := widget.NewCheck("Remove video?", func(b bool) { + newDownload.AudioOnly = b + }) + // [] Remove video? + + groupVideoDownload := container.NewVBox(downloadSettingLabelVideo, boxQualitySettings, boxCodecSettings, downloadSettingRemoveVideo) + //Merge them like: + // ## Video + // Video Quality: [1080] + // Youtube Video Codec: [h264] + // [] Remove video? + + /* SECTION III: AUDIO */ + downloadSettingLabelAudio := widget.NewRichTextFromMarkdown("### Audio") + // ## Audio - labelAccordionSettingAudio := widget.NewLabel("Audio format") - selAccordionSettingAudio := widget.NewSelect([]string{"best", "mp3", "ogg", "wav", "opus"}, func(s string) { + downloadSettingLabelAudioCodec := widget.NewLabel("Audio format:") + downloadSettingSelectAudioCodec := widget.NewSelect([]string{"best", "mp3", "ogg", "wav", "opus"}, func(s string) { switch s { case "best": newDownload.AudioCodec = gobalt.Best @@ -104,96 +181,110 @@ func main() { newDownload.AudioCodec = gobalt.Opus } }) - selAccordionSettingAudio.SetSelectedIndex(0) - audioFormatSettings := container.NewHBox(labelAccordionSettingAudio, selAccordionSettingAudio) + downloadSettingSelectAudioCodec.SetSelectedIndex(0) + boxAudioSettings := container.NewHBox(downloadSettingLabelAudioCodec, downloadSettingSelectAudioCodec) + // Audio format: [best] - checkAccordionSettingRemoveAudio := widget.NewCheck("Remove audio", func(b bool) { + downloadSettingRemoveAudio := widget.NewCheck("Remove audio?", func(b bool) { newDownload.VideoOnly = b }) - checkAccordionSettingRemoveVideo := widget.NewCheck("Remove video", func(b bool) { - newDownload.AudioOnly = b - }) - checkAccordionSettingDisableMetadata := widget.NewCheck("Disable metadata", func(b bool) { - newDownload.DisableVideoMetadata = b + // [] Remove audio? + + groupAudioDownload := container.NewVBox(downloadSettingLabelAudio, boxAudioSettings, downloadSettingRemoveAudio) + //Merge them like: + // ## Audio + // Audio format: [best] + // [] Remove audio? + + /* SECTION IV: PLATFORM SPECIFIC SETTINGS */ + downloadSettingLabelPlatform := widget.NewRichTextFromMarkdown("### Platform specific") + // ## Platform specific + + downloadSettingTwitter := widget.NewCheck("Convert Tweets to gifs?", func(b bool) { + newDownload.ConvertTwitterGifs = b }) - checkAccordionSettingsFullTikTokAudio := widget.NewCheck("Full tiktok audio", func(b bool) { + downloadSettingTwitter.Checked = true + // [X] Convert Tweets to gifs? + + downloadSettingTiktok := widget.NewCheck("Full tiktok audio?", func(b bool) { newDownload.FullTikTokAudio = b }) + // [] Full tiktok audio? - labelFilenamePattern := widget.NewLabel("File name style") - selFileNamePattern := widget.NewSelect([]string{"classic", "basic", "pretty", "nerdy"}, func(s string) { - switch s { - case "basic": - newDownload.FilenamePattern = gobalt.Basic - case "classic": - newDownload.FilenamePattern = gobalt.Classic - case "nerdy": - newDownload.FilenamePattern = gobalt.Nerdy - case "pretty": - newDownload.FilenamePattern = gobalt.Pretty - } - }) - selFileNamePattern.SetSelectedIndex(2) - filenameSettings := container.NewHBox(labelFilenamePattern, selFileNamePattern) - - accordionMaster := &widget.AccordionItem{ - Title: "Download options", - Detail: container.NewVBox(checkAccordionSettingTwitter, - qualitySettings, - codecSettings, - audioFormatSettings, - checkAccordionSettingRemoveAudio, - checkAccordionSettingRemoveVideo, - checkAccordionSettingDisableMetadata, - filenameSettings, - checkAccordionSettingsFullTikTokAudio, - ), + groupPlatformSpecific := container.NewVBox(downloadSettingLabelPlatform, downloadSettingTwitter, downloadSettingTiktok) + //Merge them like: + // ## Platform specific + // [X] Convert Tweets to gifs? + // [] Full tiktok audio? + + /* SECTION V: MERGE INTO A SINGLE CONTAINER */ + leftOptions := container.NewVBox(groupGeneralDownload, groupVideoDownload) + rightOptions := container.NewVBox(groupAudioDownload, groupPlatformSpecific) + //sep := canvas.NewLine(theme.PrimaryColor()) + downloadOptions := container.NewAdaptiveGrid(2, leftOptions, rightOptions) + /* + * END OF PART I. + */ + + /* Author: LD + PART II: Layout for the tab "about". + + SECTION I: Text Definition + MODIFIED: 04/07/2024 + */ + aboutTabMainText := widget.NewRichTextFromMarkdown("# About\n\nSave what you love, no extra bullshit.\n\nUses [cobalt.tools](cobalt.tools) under the hood.\n\n### Thanks to..\nYou, Wukko, JJ & contributors") + aboutTab := container.NewTabItemWithIcon("", theme.InfoIcon(), aboutTabMainText) + /* + * END OF PART II. + */ + + /* Author: LD + PART III: Layout for the tab "settings". + + SECTION I: GET SETTINGS + MODIFIED: 04/07/2024 + */ + tabSettingsSelectInstance := &widget.Select{ + Selected: storedInstance, + Options: cobaltInstances, } - accordionOptions := &widget.Accordion{ - Items: []*widget.AccordionItem{accordionMaster}, - MultiOpen: true, + /* SECTION II: CREATE LAYOUT TEXT + INSTANCE CHANGER */ + tabSettingsLabelInstance := widget.NewRichTextFromMarkdown("# Settings\n\nThis settings allows you to use a custom instance.\n\nYou might want to change this if you're getting any issues with the selected instance.") + tabSettingsSelectInstance.OnChanged = func(s string) { + GualtoApp.Preferences().SetString("instance", s) } - /* ABOUt & SETTINGS BUTTONS AT THE END */ - aboutButton := widget.NewButtonWithIcon("about", theme.InfoIcon(), func() { - blockClip = true - infoTextTitle := widget.NewRichTextFromMarkdown("## Gualto") - infoExit := widget.NewButtonWithIcon("", theme.CancelIcon(), nil) - infoExit.Importance = widget.DangerImportance - infoHeader := container.NewBorder(nil, nil, infoTextTitle, infoExit) - infoText := widget.NewRichTextFromMarkdown("Save what you love, no extra bullshit.\n\nUses [cobalt.tools](cobalt.tools) under the hood.\n\n### Thanks to..\nYou, Wukko, JJ & contributors") - info := widget.NewModalPopUp(container.NewBorder(infoHeader, nil, nil, nil, infoText), GualtoWin.Canvas()) - infoExit.OnTapped = func() { info.Hide(); blockClip = false } - info.Show() + /* SECTION III: OPTION TO SCAN CLIPBOARD FOR DOWNLOADABLE LINKS */ + tabSettingsCheckClip := widget.NewCheck("Check clipboard for links to download?", func(b bool) { + GualtoApp.Preferences().SetBool("clipboard", b) }) - aboutButton.Importance = widget.HighImportance + tabSettingsCheckClip.Checked = checkClipboard - //Settings - settingsButton := widget.NewButtonWithIcon("settings", theme.SettingsIcon(), func() { - blockClip = true - storedInstance := gualtoApp.Preferences().StringWithFallback("instance", gobalt.CobaltApi) - checkClipboard := gualtoApp.Preferences().BoolWithFallback("clipboard", true) - changeInstancesList := &widget.Select{ - Selected: storedInstance, - Options: instancesList, - } - changeInstancesLabel := widget.NewLabel("This allows you to use a custom instance.\nOnly change if you know what you are doing!") - changeInstancesList.OnChanged = func(s string) { - gualtoApp.Preferences().SetString("instance", s) + /* SECTION IV: CUSTOM COBALT THEME? */ + tabSettingsCustomTheme := widget.NewCheck("Use new app theme?", func(b bool) { + GualtoApp.Preferences().SetBool("theme", b) + if b { + GualtoApp.Settings().SetTheme(cobaltTheme{}) + } else { + GualtoApp.Settings().SetTheme(theme.DefaultTheme()) } - shouldCheckClipboard := widget.NewCheck("Check clipboard for media to download?", func(b bool) { - gualtoApp.Preferences().SetBool("clipboard", b) - }) - shouldCheckClipboard.Checked = checkClipboard - settingsDialog := dialog.NewCustom("Gualto App Settings", "Close", container.NewVBox(changeInstancesLabel, changeInstancesList, widget.NewSeparator(), shouldCheckClipboard), GualtoWin) - settingsDialog.Show() - settingsDialog.SetOnClosed(func() { - blockClip = false - }) }) - settingsButton.IconPlacement = widget.ButtonIconTrailingText - settingsButton.Importance = widget.HighImportance + tabSettingsCustomTheme.Checked = newTheme + + /* SECTION V: REMEMBER LAST PATH WHERE IT WAS DOWNLOADED? */ + /* DESKTOP ONLY: ANDROID & iOS PICKER ALREADY DOES THAT */ + tabSettingsLastPath := widget.NewCheck("Remember last folder saved?", func(b bool) { + GualtoApp.Preferences().SetBool("remember-path", b) + }) + tabSettingsLastPath.Checked = shouldRememberPath + if fyne.CurrentApp().Driver().Device().IsMobile() { + tabSettingsLastPath.Hide() + } + + tabSettingsContent := container.NewVBox(tabSettingsLabelInstance, tabSettingsSelectInstance, widget.NewSeparator(), tabSettingsCheckClip, widget.NewSeparator(), tabSettingsCustomTheme, widget.NewSeparator(), tabSettingsLastPath) + settingsTab := container.NewTabItemWithIcon("", theme.SettingsIcon(), tabSettingsContent) + submitURL.OnTapped = func() { blockClip = true submitURL.Disable() @@ -218,14 +309,24 @@ func main() { /* CREATE THE FINAL LAYOUT AND DISPLAY */ submitContainer := container.NewBorder(nil, nil, nil, submitURL, pasteURL) - downloadActions := container.NewVBox(labelMain, widget.NewSeparator(), submitContainer) - aboutSettings := container.NewGridWithColumns(2, aboutButton, settingsButton) - windowContent := container.NewBorder(downloadActions, aboutSettings, nil, nil, container.NewScroll(accordionOptions)) - GualtoWin.SetContent(windowContent) + downloadActions := container.NewVBox(labelMain, submitContainer) + tabMainContent := container.NewBorder(downloadActions, nil, nil, nil, container.NewScroll(downloadOptions)) + mainTab := container.NewTabItemWithIcon("", theme.HomeIcon(), tabMainContent) + + layoutTabs := container.NewAppTabs(mainTab, settingsTab, aboutTab) + layoutTabs.OnSelected = func(ti *container.TabItem) { + if layoutTabs.SelectedIndex() == 1 && len(cobaltInstances) > 1 { + tabSettingsSelectInstance.SetOptions(cobaltInstances) //Fix to set cobalt instances, for some reason it's not being set anymore. + } + } + layoutTabs.SetTabLocation(container.TabLocationLeading) + + GualtoWin.SetContent(layoutTabs) + + GualtoApp.Lifecycle().SetOnEnteredForeground(func() { - gualtoApp.Lifecycle().SetOnEnteredForeground(func() { go func() { - if !blockClip && gualtoApp.Preferences().Bool("clipboard") { //Show clipboard paste if all of these are true + if !blockClip && GualtoApp.Preferences().Bool("clipboard") { //Show clipboard paste if all of these are true blockClip = true isLink := verifyLink.MatchString(GualtoWin.Clipboard().Content()) if !isLink { @@ -269,9 +370,10 @@ func downloadMedia(options gobalt.Settings) error { } normalizeFileName := regexp.MustCompile(`[^[:word:][:punct:]\s]`) normalFileName := normalizeFileName.ReplaceAllString(mediaFilename, "") - fmt.Printf("old %v, new: %v\n", mediaFilename, normalFileName) saveFileDialog := dialog.NewFileSave(func(uc fyne.URIWriteCloser, err error) { + savingFile := dialog.NewProgressInfinite("Downloading your file...", "might take a while.", GualtoWin) + savingFile.Show() blockClip = true if err != nil || uc == nil { return @@ -282,9 +384,18 @@ func downloadMedia(options gobalt.Settings) error { return } cobaltMediaResponse.Body.Close() + if GualtoApp.Preferences().Bool("remember-path") { + GualtoApp.Preferences().SetString("path", uc.URI().Path()) + } + savingFile.Hide() dialog.ShowInformation(fmt.Sprintf("Media (%d.2MB) saved with success!", (fromReqToFile/1000000)), fmt.Sprintf("Saved to %v", uc.URI().Path()), GualtoWin) blockClip = false }, GualtoWin) + if GualtoApp.Preferences().String("path") != "" { + u, _ := storage.ParseURI(GualtoApp.Preferences().String("path")) + ul, _ := storage.ListerForURI(u) + saveFileDialog.SetLocation(ul) + } saveFileDialog.SetFileName(normalFileName) go saveFileDialog.Show() return nil diff --git a/theme_cobalt.go b/theme_cobalt.go new file mode 100644 index 0000000..3f01b67 --- /dev/null +++ b/theme_cobalt.go @@ -0,0 +1,90 @@ +// Code generated by fyne-theme-generator + +package main + +import ( + "image/color" + + "fyne.io/fyne/v2" + "fyne.io/fyne/v2/theme" +) + +type cobaltTheme struct{} + +func (cobaltTheme) Color(c fyne.ThemeColorName, v fyne.ThemeVariant) color.Color { + switch c { + case theme.ColorNameBackground: + return color.NRGBA{R: 0x15, G: 0x15, B: 0x15, A: 0xff} + case theme.ColorNameButton: + return color.NRGBA{R: 0x18, G: 0x18, B: 0x18, A: 0xfc} + case theme.ColorNameDisabledButton: + return color.NRGBA{R: 0x26, G: 0x26, B: 0x26, A: 0xff} + case theme.ColorNameDisabled: + return color.NRGBA{R: 0xff, G: 0xff, B: 0xff, A: 0x42} + case theme.ColorNameError: + return color.NRGBA{R: 0xf5, G: 0x12, B: 0x1, A: 0xff} + case theme.ColorNameFocus: + return color.NRGBA{R: 0x6d, G: 0x6b, B: 0x6b, A: 0xed} + case theme.ColorNameForeground: + return color.NRGBA{R: 0xff, G: 0xff, B: 0xff, A: 0xff} + case theme.ColorNameHover: + return color.NRGBA{R: 0x57, G: 0x59, B: 0x5b, A: 0xff} + case theme.ColorNameInputBackground: + return color.NRGBA{R: 0xff, G: 0xff, B: 0xff, A: 0x2b} + case theme.ColorNamePlaceHolder: + return color.NRGBA{R: 0xb2, G: 0xb2, B: 0xb2, A: 0xff} + case theme.ColorNamePressed: + return color.NRGBA{R: 0xff, G: 0xff, B: 0xff, A: 0xe2} + case theme.ColorNamePrimary: + return color.NRGBA{R: 0xbc, G: 0xb8, B: 0xbb, A: 0xff} + case theme.ColorNameScrollBar: + return color.NRGBA{R: 0xf1, G: 0xef, B: 0xef, A: 0x99} + case theme.ColorNameShadow: + return color.NRGBA{R: 0xa, G: 0x8, B: 0x8, A: 0x88} + default: + return theme.DefaultTheme().Color(c, v) + } +} + +func (cobaltTheme) Font(s fyne.TextStyle) fyne.Resource { + if s.Monospace { + return theme.DefaultTheme().Font(s) + } + if s.Bold { + if s.Italic { + return theme.DefaultTheme().Font(s) + } + return theme.DefaultTheme().Font(s) + } + if s.Italic { + return theme.DefaultTheme().Font(s) + } + return theme.DefaultTheme().Font(s) +} + +func (cobaltTheme) Icon(n fyne.ThemeIconName) fyne.Resource { + return theme.DefaultTheme().Icon(n) +} + +func (cobaltTheme) Size(s fyne.ThemeSizeName) float32 { + switch s { + case theme.SizeNameCaptionText: + return 11 + case theme.SizeNameInlineIcon: + return 20 + case theme.SizeNamePadding: + return 4 + case theme.SizeNameScrollBar: + return 16 + case theme.SizeNameScrollBarSmall: + return 3 + case theme.SizeNameSeparatorThickness: + return 1 + case theme.SizeNameText: + return 14 + case theme.SizeNameInputBorder: + return 2 + default: + return theme.DefaultTheme().Size(s) + } +}