Compare commits

...

23 Commits

Author SHA1 Message Date
76dbbacd98 Bump version 2024-04-30 22:22:13 +09:30
ba31f52ee2 Use zip for windows releases 2022-12-05 23:44:06 +10:30
779f9e1992 Goodbye perl build script. 2022-12-05 23:36:16 +10:30
553337bfab Migrate to goreleaser for next version 2022-12-05 23:24:27 +10:30
73c74cd37e Start to rework tests 2022-12-05 22:04:40 +10:30
2bd62a95a4 Add arm binaries for mac and linux 2022-12-05 22:03:11 +10:30
3497bfd194 Add state reasons 2022-12-05 22:01:52 +10:30
ada43b176b Refactor image processing for more flexibility. Add support for resizing images if they exceed the discord 8Mb limit. 2022-11-01 13:06:16 +10:30
326807b395 Fix documentation on watch intervals. Fixes #26 2022-09-15 22:14:59 +09:30
4dd166e65e Update changelog for release 2022-05-09 09:57:26 +09:30
9f7090a2f8 Bump version 2022-05-08 11:39:54 +09:30
1bf557c3eb Update changelog 2022-05-08 11:39:14 +09:30
df0c6d090d Fix race condition causing multiple uploads 2022-05-08 11:38:07 +09:30
b851a4f773 🙏🏼 2022-05-02 21:21:00 +09:30
e612f8ae59 Update README 2022-05-01 17:47:47 +09:30
05ddf07fc4 Update (and fix old typos) in changelog 2022-05-01 17:42:23 +09:30
9423bc32e9 Automate the population of the windows exe metadata 2022-05-01 17:39:00 +09:30
243c349366 Tidy up, add discord link 2022-05-01 17:01:02 +09:30
d8b16674dd Only do systray stuff on windows 2022-05-01 16:51:52 +09:30
9294ca9e33 Windows exe resource stuff 2022-05-01 15:50:27 +09:30
2a99541f58 go mod tidy 2022-05-01 14:40:40 +09:30
b04ed6aa62 Add systray with icon 2022-05-01 12:37:05 +09:30
06ac259e0a Open browser on startup automatically, with configuration option to disable. 2022-05-01 11:55:20 +09:30
28 changed files with 724 additions and 248 deletions

1
.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1 @@
github: tardisx

1
.gitignore vendored
View File

@ -5,3 +5,4 @@ discord-auto-upload.exe
*.png
*.jpg
.DS_Store
versioninfo.json

45
.goreleaser.yaml Normal file
View File

@ -0,0 +1,45 @@
before:
hooks:
# clean up/install modules
- go mod tidy
- "go run tools/windows_metadata/release.go v{{- .Version }}"
builds:
- main:
hooks:
pre:
- "rm -f resource.syso"
- "go generate"
ignore:
- goos: windows
goarch: arm64
ldflags:
- '{{ if eq .Os "windows" }}-H=windowsgui{{ end }}'
env:
- CGO_ENABLED=0
goos:
- linux
- windows
- darwin
archives:
- replacements:
darwin: Darwin
linux: Linux
windows: Windows
386: i386
amd64: x86_64
format_overrides:
- goos: windows
format: zip
checksum:
name_template: 'checksums.txt'
snapshot:
name_template: "{{ incpatch .Version }}-next"
changelog:
sort: asc
filters:
exclude:
- '^Merge:'
- '^docs:'
- '^test:'
- '^[Bb]ump'
- '^[Cc]lean'

View File

@ -1,6 +1,10 @@
{
"cSpell.words": [
"daulog",
"inconsolata"
"Debugf",
"inconsolata",
"Infof",
"Markedup",
"skratchdot"
]
}

View File

@ -3,12 +3,33 @@ All notable changes to this project will be documented in this file.
## [Unreleased]
## [v0.12.1] - 2020-05-01
## [v0.13.0] - 2022-11-01
- Resize images if needed to fit in discord 8Mb upload limits
## [v0.12.4] - 2022-09-15
- Document that watcher intervals are in seconds
## [v0.12.3] - 2022-05-09
- Fix a race condition occasionally causing multiple duplicate uploads
## [v0.12.2] - 2022-05-01
- Automatically open your web browser to the `dau` web interface
(can be disabled in configuration)
- Add system tray/menubar icon with menus to open web interface, quit and
other links
- Superfluous text console removed on windows
## [v0.12.1] - 2022-05-01
- Show if a new version is available in the web interface
- Rework logging and fix the log display in the web interface
## [v0.12.0] - 2020-04-03
## [v0.12.0] - 2022-04-03
- Break upload page into pending/current/complete sections
- Add preview thumbnails for each upload

View File

@ -29,8 +29,13 @@ You'll need to [download Go](https://golang.org/dl/), check the code out somewhe
## Using it
`dau` configuration is managed via its internal web interface. When the executable is run, you can visit
`http://localhost:9090` in your web browser to configure the it. Configuration persists across runs, it is
saved in a file called '.dau.json' in your home directory.
`http://localhost:9090` in your web browser to configure it. On Windows, a tray icon is created to provide
access to the web interface.
The web browser will be loaded automatically when you start the program, if possible. This option can be
disabled in the settings.
Configuration persists across runs, it is saved in a file called '.dau.json' in your home directory.
The first time you run it, you will need to configure at least the discord web hook and the watch path for
`dau` to be useful.
@ -49,7 +54,7 @@ no changes will take effect until the "Save All Configuration" button has been p
### Global options
* Server port - the port number the web server listens on. Requires restart
* Watch interval - how often each watcher will check the directory for new files
* Watch interval - how often each watcher will check the directory for new files, in seconds
### Watcher configuration

View File

@ -1,45 +0,0 @@
#!/usr/bin/env perl
use strict;
use warnings;
open my $fh, "<", "version/version.go" || die $!;
my $version;
while (<$fh>) {
$version = $1 if /^const\s+CurrentVersion.*?"(v[\d\.]+)"/;
}
close $fh;
die "no version?" unless defined $version;
# quit if tests fail
system("go test ./...") && die "not building release with failing tests";
# so lazy
system "rm", "-rf", "release", "dist";
system "mkdir", "release";
system "mkdir", "dist";
my %build = (
win => { env => { GOOS => 'windows', GOARCH => 'amd64' }, filename => 'dau.exe' },
linux => { env => { GOOS => 'linux', GOARCH => 'amd64' }, filename => 'dau' },
mac => { env => { GOOS => 'darwin', GOARCH => 'amd64' }, filename => 'dau' },
);
foreach my $type (keys %build) {
mkdir "release/$type";
}
add_extras();
foreach my $type (keys %build) {
local $ENV{GOOS} = $build{$type}->{env}->{GOOS};
local $ENV{GOARCH} = $build{$type}->{env}->{GOARCH};
system "go", "build", "-o", "release/$type/" . $build{$type}->{filename};
system "zip", "-j", "dist/dau-$type-$version.zip", ( glob "release/$type/*" );
}
sub add_extras {
# we used to have a .bat file here, but no longer needed
}

View File

@ -38,8 +38,16 @@ type ConfigV2 struct {
Watchers []Watcher
}
type ConfigV3 struct {
WatchInterval int
Version int
Port int
OpenBrowserOnStart bool
Watchers []Watcher
}
type ConfigService struct {
Config *ConfigV2
Config *ConfigV3
Changed chan bool
ConfigFilename string
}
@ -66,11 +74,12 @@ func (c *ConfigService) LoadOrInit() error {
}
}
func DefaultConfig() *ConfigV2 {
c := ConfigV2{}
c.Version = 2
func DefaultConfig() *ConfigV3 {
c := ConfigV3{}
c.Version = 3
c.WatchInterval = 10
c.Port = 9090
c.OpenBrowserOnStart = true
w := Watcher{
WebHookURL: "https://webhook.url.here",
Path: "/your/screenshot/dir/here",
@ -122,6 +131,13 @@ func (c *ConfigService) Load() error {
c.Config.Watchers = []Watcher{onlyWatcher}
}
if c.Config.Version == 2 {
// need to migrate this
daulog.Info("Migrating config to V3")
c.Config.Version = 3
c.Config.OpenBrowserOnStart = true
}
return nil
}

View File

@ -22,8 +22,8 @@ func TestNoConfig(t *testing.T) {
t.Errorf("unexpected failure from load: %s", err)
}
if c.Config.Version != 2 {
t.Error("not version 2 starting config")
if c.Config.Version != 3 {
t.Error("not version 3 starting config")
}
if fileSize(c.ConfigFilename) < 40 {
@ -45,7 +45,7 @@ func TestEmptyFileConfig(t *testing.T) {
}
func TestMigrateFromV1toV2(t *testing.T) {
func TestMigrateFromV1toV3(t *testing.T) {
c := ConfigService{}
c.ConfigFilename = v1Config()
@ -53,8 +53,12 @@ func TestMigrateFromV1toV2(t *testing.T) {
if err != nil {
t.Error("unexpected error from LoadOrInit()")
}
if c.Config.Version != 2 {
t.Errorf("Version %d not 2", c.Config.Version)
if c.Config.Version != 3 {
t.Errorf("Version %d not 3", c.Config.Version)
}
if c.Config.OpenBrowserOnStart != true {
t.Errorf("Open browser on start not true")
}
if len(c.Config.Watchers) != 1 {

29
dau.go
View File

@ -15,12 +15,12 @@ import (
_ "image/jpeg"
_ "image/png"
// "github.com/skratchdot/open-golang/open"
"github.com/tardisx/discord-auto-upload/config"
daulog "github.com/tardisx/discord-auto-upload/log"
"github.com/tardisx/discord-auto-upload/upload"
"github.com/skratchdot/open-golang/open"
// "github.com/tardisx/discord-auto-upload/upload"
"github.com/tardisx/discord-auto-upload/version"
"github.com/tardisx/discord-auto-upload/web"
@ -37,20 +37,24 @@ func main() {
parseOptions()
// grab the config, register to notice changes
config := config.DefaultConfigService()
// grab the conf, register to notice changes
conf := config.DefaultConfigService()
configChanged := make(chan bool)
config.Changed = configChanged
config.LoadOrInit()
conf.Changed = configChanged
conf.LoadOrInit()
// create the uploader
up := upload.NewUploader()
// log.Print("Opening web browser")
// open.Start("http://localhost:9090")
web := web.WebService{Config: config, Uploader: up}
web := web.WebService{Config: conf, Uploader: up}
web.StartWebServer()
if conf.Config.OpenBrowserOnStart {
openWebBrowser(conf.Config.Port)
}
go func() {
version.GetOnlineVersion()
if version.UpdateAvailable() {
@ -65,7 +69,10 @@ func main() {
// create the watchers, restart them if config changes
// blocks forever
startWatchers(config, up, configChanged)
go func() {
startWatchers(conf, up, configChanged)
}()
mainloop(conf)
}
@ -122,6 +129,7 @@ func (w *watch) ProcessNewFiles() []string {
}
w.lastCheck = w.newLastCheck
}
return newFiles
}
@ -188,3 +196,8 @@ func parseOptions() {
}
}
func openWebBrowser(port int) {
address := fmt.Sprintf("http://localhost:%d", port)
open.Start(address)
}

BIN
dau.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

12
dau_nonwindows.go Normal file
View File

@ -0,0 +1,12 @@
//go:build darwin || linux
// +build darwin linux
package main
import "github.com/tardisx/discord-auto-upload/config"
func mainloop(c *config.ConfigService) {
ch := make(chan bool)
<-ch
}

60
dau_windows.go Normal file
View File

@ -0,0 +1,60 @@
package main
import (
_ "embed"
"fmt"
"github.com/getlantern/systray"
"github.com/skratchdot/open-golang/open"
"github.com/tardisx/discord-auto-upload/config"
daulog "github.com/tardisx/discord-auto-upload/log"
"github.com/tardisx/discord-auto-upload/version"
)
//go:generate goversioninfo
//go:embed dau.ico
var appIcon []byte
func mainloop(c *config.ConfigService) {
systray.Run(func() { onReady(c) }, onExit)
}
func onReady(c *config.ConfigService) {
systray.SetIcon(appIcon)
//systray.SetTitle("DAU")
systray.SetTooltip(fmt.Sprintf("discord-auto-upload %s", version.CurrentVersion))
openApp := systray.AddMenuItem("Open", "Open in web browser")
gh := systray.AddMenuItem("Github", "Open project page")
discord := systray.AddMenuItem("Discord", "Join us on discord")
ghr := systray.AddMenuItem("Release Notes", "Open project release notes")
quit := systray.AddMenuItem("Quit", "Quit")
go func() {
<-quit.ClickedCh
systray.Quit()
}()
go func() {
for {
select {
case <-openApp.ClickedCh:
openWebBrowser(c.Config.Port)
case <-gh.ClickedCh:
open.Start("https://github.com/tardisx/discord-auto-upload")
case <-ghr.ClickedCh:
open.Start(fmt.Sprintf("https://github.com/tardisx/discord-auto-upload/releases/tag/%s", version.CurrentVersion))
case <-discord.ClickedCh:
open.Start("https://discord.gg/eErG9sntbZ")
}
}
}()
// Sets the icon of a menu item. Only available on Mac and Windows.
// mQuit.SetIcon(icon.Data)
}
func onExit() {
// clean up here
daulog.Info("quitting on user request")
}

7
go.mod
View File

@ -4,9 +4,12 @@ go 1.16
require (
github.com/fogleman/gg v1.3.0
github.com/getlantern/systray v1.2.1
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
github.com/gorilla/mux v1.8.0 // indirect
github.com/gorilla/mux v1.8.0
github.com/mitchellh/go-homedir v1.1.0
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966
github.com/stretchr/testify v1.6.1 // indirect
golang.org/x/image v0.0.0-20210504121937-7319ad40d33e
golang.org/x/mod v0.4.2
golang.org/x/mod v0.7.0
)

57
go.sum
View File

@ -1,23 +1,68 @@
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8=
github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
github.com/getlantern/context v0.0.0-20190109183933-c447772a6520 h1:NRUJuo3v3WGC/g5YiyF790gut6oQr5f3FBI88Wv0dx4=
github.com/getlantern/context v0.0.0-20190109183933-c447772a6520/go.mod h1:L+mq6/vvYHKjCX2oez0CgEAJmbq1fbb/oNJIWQkBybY=
github.com/getlantern/errors v0.0.0-20190325191628-abdb3e3e36f7 h1:6uJ+sZ/e03gkbqZ0kUG6mfKoqDb4XMAzMIwlajq19So=
github.com/getlantern/errors v0.0.0-20190325191628-abdb3e3e36f7/go.mod h1:l+xpFBrCtDLpK9qNjxs+cHU6+BAdlBaxHqikB6Lku3A=
github.com/getlantern/golog v0.0.0-20190830074920-4ef2e798c2d7 h1:guBYzEaLz0Vfc/jv0czrr2z7qyzTOGC9hiQ0VC+hKjk=
github.com/getlantern/golog v0.0.0-20190830074920-4ef2e798c2d7/go.mod h1:zx/1xUUeYPy3Pcmet8OSXLbF47l+3y6hIPpyLWoR9oc=
github.com/getlantern/hex v0.0.0-20190417191902-c6586a6fe0b7 h1:micT5vkcr9tOVk1FiH8SWKID8ultN44Z+yzd2y/Vyb0=
github.com/getlantern/hex v0.0.0-20190417191902-c6586a6fe0b7/go.mod h1:dD3CgOrwlzca8ed61CsZouQS5h5jIzkK9ZWrTcf0s+o=
github.com/getlantern/hidden v0.0.0-20190325191715-f02dbb02be55 h1:XYzSdCbkzOC0FDNrgJqGRo8PCMFOBFL9py72DRs7bmc=
github.com/getlantern/hidden v0.0.0-20190325191715-f02dbb02be55/go.mod h1:6mmzY2kW1TOOrVy+r41Za2MxXM+hhqTtY3oBKd2AgFA=
github.com/getlantern/ops v0.0.0-20190325191751-d70cb0d6f85f h1:wrYrQttPS8FHIRSlsrcuKazukx/xqO/PpLZzZXsF+EA=
github.com/getlantern/ops v0.0.0-20190325191751-d70cb0d6f85f/go.mod h1:D5ao98qkA6pxftxoqzibIBBrLSUli+kYnJqrgBf9cIA=
github.com/getlantern/systray v1.2.1 h1:udsC2k98v2hN359VTFShuQW6GGprRprw6kD6539JikI=
github.com/getlantern/systray v1.2.1/go.mod h1:AecygODWIsBquJCJFop8MEQcJbWFfw/1yWbVabNgpCM=
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c h1:rp5dCmg/yLR3mgFuSOe4oEnDDmGLROTvMragMUXpTQw=
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c/go.mod h1:X07ZCGwUbLaax7L0S3Tw4hpejzu63ZrrQiUe6W0hcy0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 h1:JIAuq3EEf9cgbU6AtGPK4CTG3Zf6CKMNqf0MHTggAUA=
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/image v0.0.0-20210504121937-7319ad40d33e h1:PzJMNfFQx+QO9hrC1GwZ4BoPGeNGhfeQEgcQFArEjPk=
golang.org/x/image v0.0.0-20210504121937-7319ad40d33e/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA=
golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

188
image/image.go Normal file
View File

@ -0,0 +1,188 @@
// Package image is responsible for thumbnailing, resizing and watermarking
// images.
package image
import (
"fmt"
i "image"
"image/jpeg"
"image/png"
"io"
"os"
daulog "github.com/tardisx/discord-auto-upload/log"
"golang.org/x/image/draw"
)
// the filenames below are ordered in a specific way
// In the simplest case we only need the original filename.
// In more complex cases, we might have other files, created
// temporarily. These all need to be cleaned up.
// We upload the "final" file, depending on what actions have
// been taken.
type Store struct {
OriginalFilename string
OriginalFormat string // jpeg, png
ModifiedFilename string // if the user applied modifications
ResizedFilename string // if the file had to be resized to be uploaded
WatermarkedFilename string
MaxBytes int
Watermark bool
}
// ReadCloser returns an io.ReadCloser providing the imagedata
// with the manglings that have been requested
func (s *Store) ReadCloser() (io.ReadCloser, error) {
// determine format
s.determineFormat()
// check if we will fit the number of bytes, resize if necessary
err := s.resizeToUnder(int64(s.MaxBytes))
if err != nil {
return nil, err
}
// the conundrum here is that the watermarking could modify the file size again, maybe going over
// the MaxBytes size. That would mostly be about jpeg compression levels I guess...
if s.Watermark {
s.applyWatermark()
}
// return the reader
f, err := os.Open(s.uploadSourceFilename())
if err != nil {
return nil, err
}
return f, nil
}
func (s *Store) determineFormat() error {
file, err := os.Open(s.OriginalFilename)
if err != nil {
panic(fmt.Errorf("could not open file: %s", err))
}
defer file.Close()
_, format, err := i.Decode(file)
if err != nil {
panic(fmt.Errorf("could not decode file: %s", err))
}
s.OriginalFormat = format
return nil
}
// resizeToUnder resizes the image, if necessary
func (s *Store) resizeToUnder(size int64) error {
fileToResize := s.uploadSourceFilename()
fi, err := os.Stat(s.uploadSourceFilename())
if err != nil {
return err
}
currentSize := fi.Size()
if currentSize <= size {
return nil // nothing needs to be done
}
daulog.Infof("%s is %d bytes, need to resize to fit in %d", fileToResize, currentSize, size)
file, err := os.Open(fileToResize)
if err != nil {
panic(fmt.Errorf("could not open file: %s", err))
}
defer file.Close()
im, _, err := i.Decode(file)
if err != nil {
panic(fmt.Errorf("could not decode file: %s", err))
}
// if the size is 10% too big, we reduce X and Y by 10% - this is overkill but should
// get us across the line in most cases
fraction := float64(currentSize) / float64(size) // say 1.1 for 10%
newXY := i.Point{
X: int(float64(im.Bounds().Max.X) / fraction),
Y: int(float64(im.Bounds().Max.Y) / fraction),
}
daulog.Infof("fraction is %f, will resize to %dx%d", fraction, newXY.X, newXY.Y)
dst := i.NewRGBA(i.Rect(0, 0, newXY.X, newXY.Y))
draw.BiLinear.Scale(dst, dst.Rect, im, im.Bounds(), draw.Over, nil)
resizedFile, err := os.CreateTemp("", "dau_resize_file_*")
if err != nil {
return err
}
if s.OriginalFormat == "png" {
err = png.Encode(resizedFile, dst)
if err != nil {
return err
}
} else if s.OriginalFormat == "jpeg" {
err = jpeg.Encode(resizedFile, dst, nil)
if err != nil {
return err
}
} else {
panic("unknown format " + s.OriginalFormat)
}
s.ResizedFilename = resizedFile.Name()
resizedFile.Close()
fi, err = os.Stat(s.uploadSourceFilename())
if err != nil {
return err
}
newSize := fi.Size()
if newSize <= size {
daulog.Infof("File resized, now %d", newSize)
return nil // nothing needs to be done
} else {
return fmt.Errorf("failed to resize: was %d, now %d, needed %d", currentSize, newSize, size)
}
}
// uploadSourceFilename gives us the filename, which might be a watermarked, resized
// or markedup version, depending on what has happened to this file.
func (s Store) uploadSourceFilename() string {
if s.WatermarkedFilename != "" {
return s.WatermarkedFilename
}
if s.ResizedFilename != "" {
return s.ResizedFilename
}
if s.ModifiedFilename != "" {
return s.ModifiedFilename
}
return s.OriginalFilename
}
// UploadFilename provides a name to be assigned to the upload on Discord
func (s Store) UploadFilename() string {
return "image." + s.OriginalFormat
}
// Cleanup removes all the temporary files that we might have created
func (s Store) Cleanup() {
daulog.Infof("cleaning temporary files %#v", s)
if s.ModifiedFilename != "" {
daulog.Infof("removing %s", s.ModifiedFilename)
os.Remove(s.ModifiedFilename)
}
if s.ResizedFilename != "" {
daulog.Infof("removing %s", s.ResizedFilename)
os.Remove(s.ResizedFilename)
}
if s.WatermarkedFilename != "" {
daulog.Infof("removing %s", s.WatermarkedFilename)
os.Remove(s.WatermarkedFilename)
}
}

View File

@ -1,14 +1,13 @@
package imageprocess
package image
import (
"fmt"
"image"
i "image"
"image/png"
"io"
"log"
"os"
"github.com/tardisx/discord-auto-upload/upload"
"golang.org/x/image/draw"
)
@ -17,15 +16,21 @@ const (
thumbnailMaxY = 128
)
func (ip *Processor) ThumbPNG(ul *upload.Upload, which string, w io.Writer) error {
type ThumbType = string
const ThumbTypeOriginal = "orig"
const ThumbTypeMarkedUp = "markedup"
// ThumbPNG writes a thumbnail out to an io.Writer
func (ip *Store) ThumbPNG(t ThumbType, w io.Writer) error {
var filename string
if which == "orig" {
filename = ul.OriginalFilename
} else if which == "markedup" {
filename = ul.MarkedUpFilename
if t == ThumbTypeOriginal {
filename = ip.OriginalFilename
} else if t == ThumbTypeMarkedUp {
filename = ip.ModifiedFilename
} else {
log.Fatal("was passed incorrect 'which' arg")
log.Fatal("was passed incorrect 'type' arg")
}
file, err := os.Open(filename)
@ -33,12 +38,12 @@ func (ip *Processor) ThumbPNG(ul *upload.Upload, which string, w io.Writer) erro
return fmt.Errorf("could not open file: %s", err)
}
defer file.Close()
im, _, err := image.Decode(file)
im, _, err := i.Decode(file)
if err != nil {
return fmt.Errorf("could not decode file: %s", err)
}
newXY := image.Point{}
newXY := i.Point{}
if im.Bounds().Max.X/thumbnailMaxX > im.Bounds().Max.Y/thumbnailMaxY {
newXY.X = thumbnailMaxX
newXY.Y = im.Bounds().Max.Y / (im.Bounds().Max.X / thumbnailMaxX)
@ -47,7 +52,7 @@ func (ip *Processor) ThumbPNG(ul *upload.Upload, which string, w io.Writer) erro
newXY.X = im.Bounds().Max.X / (im.Bounds().Max.Y / thumbnailMaxY)
}
dst := image.NewRGBA(image.Rect(0, 0, newXY.X, newXY.Y))
dst := i.NewRGBA(i.Rect(0, 0, newXY.X, newXY.Y))
draw.BiLinear.Scale(dst, dst.Rect, im, im.Bounds(), draw.Over, nil)
png.Encode(w, dst)

64
image/watermark.go Normal file
View File

@ -0,0 +1,64 @@
package image
import (
"fmt"
i "image"
"image/jpeg"
"image/png"
"os"
daulog "github.com/tardisx/discord-auto-upload/log"
"github.com/fogleman/gg"
"golang.org/x/image/font/inconsolata"
)
// applyWatermark applies the watermark to the image
func (s *Store) applyWatermark() error {
in, err := os.Open(s.uploadSourceFilename())
defer in.Close()
im, _, err := i.Decode(in)
if err != nil {
daulog.Errorf("Cannot decode image: %v - skipping watermarking", err)
return fmt.Errorf("cannot decode image: %w", err)
}
bounds := im.Bounds()
// var S float64 = float64(bounds.Max.X)
dc := gg.NewContext(bounds.Max.X, bounds.Max.Y)
dc.Clear()
dc.SetRGB(0, 0, 0)
dc.SetFontFace(inconsolata.Regular8x16)
dc.DrawImage(im, 0, 0)
dc.DrawRoundedRectangle(0, float64(bounds.Max.Y-18.0), 320, float64(bounds.Max.Y), 0)
dc.SetRGB(0, 0, 0)
dc.Fill()
dc.SetRGB(1, 1, 1)
dc.DrawString("github.com/tardisx/discord-auto-upload", 5.0, float64(bounds.Max.Y)-5.0)
waterMarkedFile, err := os.CreateTemp("", "dau_watermark_file_*")
if err != nil {
return err
}
defer waterMarkedFile.Close()
if s.OriginalFormat == "png" {
png.Encode(waterMarkedFile, dc.Image())
} else if s.OriginalFormat == "jpeg" {
jpeg.Encode(waterMarkedFile, dc.Image(), nil)
} else {
panic("Cannot handle " + s.OriginalFormat)
}
s.WatermarkedFilename = waterMarkedFile.Name()
return nil
}

View File

@ -1,4 +0,0 @@
package imageprocess
type Processor struct {
}

View File

@ -0,0 +1,84 @@
package main
import (
"log"
"os"
"regexp"
"strings"
"golang.org/x/mod/semver"
)
const versionInfoTemplate = `
{
"FixedFileInfo": {
"FileVersion": {
"Major": MAJOR,
"Minor": MINOR,
"Patch": PATCH,
"Build": 0
},
"ProductVersion": {
"Major": MAJOR,
"Minor": MINOR,
"Patch": PATCH,
"Build": 0
},
"FileFlagsMask": "3f",
"FileFlags ": "00",
"FileOS": "040004",
"FileType": "01",
"FileSubType": "00"
},
"StringFileInfo": {
"Comments": "",
"CompanyName": "tardisx@github",
"FileDescription": "https://github.com/tardisx/discord-auto-upload",
"FileVersion": "",
"InternalName": "",
"LegalCopyright": "https://github.com/tardisx/discord-auto-upload/blob/master/LICENSE",
"LegalTrademarks": "",
"OriginalFilename": "",
"PrivateBuild": "",
"ProductName": "discord-auto-upload",
"ProductVersion": "VERSION",
"SpecialBuild": ""
},
"VarFileInfo": {
"Translation": {
"LangID": "0409",
"CharsetID": "04B0"
}
},
"IconPath": "dau.ico",
"ManifestPath": ""
}
`
var nonAlphanumericRegex = regexp.MustCompile(`[^0-9]+`)
func main() {
version := os.Args[1]
if !semver.IsValid(version) {
panic("bad version" + version)
}
parts := strings.Split(version, ".")
if len(parts) < 3 {
log.Fatalf("bad version: %s", version)
}
parts[0] = nonAlphanumericRegex.ReplaceAllString(parts[0], "")
parts[1] = nonAlphanumericRegex.ReplaceAllString(parts[1], "")
parts[2] = nonAlphanumericRegex.ReplaceAllString(parts[2], "")
out := versionInfoTemplate
out = strings.Replace(out, "MAJOR", parts[0], -1)
out = strings.Replace(out, "MINOR", parts[1], -1)
out = strings.Replace(out, "PATCH", parts[2], -1)
out = strings.Replace(out, "VERSION", version, -1)
f, _ := os.Create("versioninfo.json")
f.Write([]byte(out))
f.Close()
}

View File

@ -7,32 +7,31 @@ import (
"encoding/json"
"errors"
"fmt"
"image"
"io"
"io/ioutil"
"log"
"mime/multipart"
"net/http"
"os"
"path/filepath"
"sync"
"sync/atomic"
"time"
"github.com/fogleman/gg"
"github.com/tardisx/discord-auto-upload/config"
"github.com/tardisx/discord-auto-upload/image"
daulog "github.com/tardisx/discord-auto-upload/log"
"golang.org/x/image/font/inconsolata"
)
type State string
const (
StatePending State = "Pending" // waiting for decision to upload (could be edited)
StateQueued State = "Queued" // ready for upload
StateUploading State = "Uploading" // uploading
StateComplete State = "Complete" // finished successfully
StateFailed State = "Failed" // failed
StateSkipped State = "Skipped" // user did not want to upload
StatePending State = "Pending" // waiting for decision to upload (could be edited)
StateQueued State = "Queued" // ready for upload
StateWatermarking State = "Adding Watermark" // thumbnail generation
StateUploading State = "Uploading" // uploading
StateComplete State = "Complete" // finished successfully
StateFailed State = "Failed" // failed
StateSkipped State = "Skipped" // user did not want to upload
)
var currentId int32
@ -43,19 +42,17 @@ type HTTPClient interface {
type Uploader struct {
Uploads []*Upload `json:"uploads"`
Lock sync.Mutex
}
type Upload struct {
Id int32 `json:"id"`
UploadedAt time.Time `json:"uploaded_at"`
OriginalFilename string `json:"original_file"` // path on the local disk
MarkedUpFilename string `json:"markedup_file"` // a temporary file, if the user did some markup
Image *image.Store
webhookURL string
watermark bool // should watermark
usernameOverride string
Url string `json:"url"` // url on the discord CDN
@ -63,7 +60,8 @@ type Upload struct {
Width int `json:"width"`
Height int `json:"height"`
State State `json:"state"`
State State `json:"state"`
StateReason string `json:"state_reason"`
Client HTTPClient `json:"-"`
}
@ -76,34 +74,46 @@ func NewUploader() *Uploader {
}
func (u *Uploader) AddFile(file string, conf config.Watcher) {
u.Lock.Lock()
atomic.AddInt32(&currentId, 1)
thisUpload := Upload{
Id: currentId,
OriginalFilename: file,
watermark: !conf.NoWatermark,
UploadedAt: time.Time{},
Image: &image.Store{OriginalFilename: file, Watermark: !conf.NoWatermark, MaxBytes: 8_000_000},
webhookURL: conf.WebHookURL,
usernameOverride: conf.Username,
Url: "",
State: StateQueued,
Client: nil,
}
// if the user wants uploads to be held for editing etc,
// set it to Pending instead
if conf.HoldUploads {
thisUpload.State = StatePending
thisUpload.StateReason = ""
}
u.Uploads = append(u.Uploads, &thisUpload)
u.Lock.Unlock()
}
// Upload uploads any files that have not yet been uploaded
func (u *Uploader) Upload() {
u.Lock.Lock()
for _, upload := range u.Uploads {
if upload.State == StateQueued {
upload.processUpload()
}
}
u.Lock.Unlock()
}
func (u *Uploader) UploadById(id int32) *Upload {
u.Lock.Lock()
defer u.Lock.Unlock()
for _, anUpload := range u.Uploads {
if anUpload.Id == int32(id) {
return anUpload
@ -112,16 +122,8 @@ func (u *Uploader) UploadById(id int32) *Upload {
return nil
}
func (u *Upload) RemoveMarkupTempFile() {
if len(u.MarkedUpFilename) > 0 {
os.Remove(u.MarkedUpFilename)
}
}
func (u *Upload) processUpload() error {
daulog.Infof("Uploading: %s", u.OriginalFilename)
baseFilename := filepath.Base(u.OriginalFilename)
daulog.Infof("Uploading: %s", u.Image.OriginalFilename)
if u.webhookURL == "" {
daulog.Error("WebHookURL is not configured - cannot upload!")
@ -153,41 +155,14 @@ func (u *Upload) processUpload() error {
for retriesRemaining > 0 {
// open an io.ReadCloser for the file we intend to upload
var filedata *os.File
var err error
if len(u.MarkedUpFilename) > 0 {
filedata, err = os.Open(u.MarkedUpFilename)
if err != nil {
daulog.Errorf("Error opening marked up file: %s", err)
retriesRemaining--
sleepForRetries(retriesRemaining)
continue
}
} else {
filedata, err = os.Open(u.OriginalFilename)
if err != nil {
daulog.Errorf("Error opening original file: %s", err)
retriesRemaining--
sleepForRetries(retriesRemaining)
continue
}
imageData, err := u.Image.ReadCloser()
if err != nil {
panic(err)
}
var imageData io.Reader
if u.watermark {
daulog.Info("Watermarking image")
imageData, err = u.applyWatermark(filedata)
if err != nil {
daulog.Errorf("Error watermarking: %s", err)
retriesRemaining--
sleepForRetries(retriesRemaining)
continue
}
} else {
imageData = filedata
}
request, err := newfileUploadRequest(u.webhookURL, extraParams, "file", baseFilename, imageData)
request, err := newfileUploadRequest(u.webhookURL, extraParams, "file", u.Image.UploadFilename(), imageData)
if err != nil {
daulog.Errorf("error creating upload request: %s", err)
return fmt.Errorf("could not create upload request: %s", err)
@ -211,6 +186,7 @@ func (u *Upload) processUpload() error {
// just fail immediately, we know this means the file was too big
daulog.Error("413 received - file too large")
u.State = StateFailed
u.StateReason = "discord API said file too large"
return errors.New("received 413 - file too large")
}
@ -263,6 +239,7 @@ func (u *Upload) processUpload() error {
u.Url = a.URL
u.State = StateComplete
u.StateReason = ""
u.Width = a.Width
u.Height = a.Height
u.UploadedAt = time.Now()
@ -271,12 +248,13 @@ func (u *Upload) processUpload() error {
}
}
// remove any marked up file
u.RemoveMarkupTempFile()
// remove any temporary files
u.Image.Cleanup()
if retriesRemaining == 0 {
daulog.Error("Failed to upload, even after all retries")
u.State = StateFailed
u.StateReason = "could not upload after all retries"
return errors.New("could not upload after all retries")
}
@ -309,40 +287,6 @@ func newfileUploadRequest(uri string, params map[string]string, paramName string
return req, err
}
// applyWatermark applies the watermark to the image
func (u *Upload) applyWatermark(in *os.File) (io.Reader, error) {
defer in.Close()
im, _, err := image.Decode(in)
if err != nil {
daulog.Errorf("Cannot decode image: %v - skipping watermarking", err)
return nil, errors.New("cannot decode image")
}
bounds := im.Bounds()
// var S float64 = float64(bounds.Max.X)
dc := gg.NewContext(bounds.Max.X, bounds.Max.Y)
dc.Clear()
dc.SetRGB(0, 0, 0)
dc.SetFontFace(inconsolata.Regular8x16)
dc.DrawImage(im, 0, 0)
dc.DrawRoundedRectangle(0, float64(bounds.Max.Y-18.0), 320, float64(bounds.Max.Y), 0)
dc.SetRGB(0, 0, 0)
dc.Fill()
dc.SetRGB(1, 1, 1)
dc.DrawString("github.com/tardisx/discord-auto-upload", 5.0, float64(bounds.Max.Y)-5.0)
b := bytes.Buffer{}
dc.EncodePNG(&b)
return &b, nil
}
func sleepForRetries(retry int) {
if retry == 0 {
return

View File

@ -2,15 +2,13 @@ package upload
import (
"bytes"
"image"
i "image"
"image/color"
"image/png"
"io/ioutil"
"math/rand"
"net/http"
"os"
"testing"
// "github.com/tardisx/discord-auto-upload/config"
)
// https://www.thegreatcodeadventure.com/mocking-http-requests-in-golang/
@ -38,52 +36,50 @@ func DoTooBigUpload(req *http.Request) (*http.Response, error) {
}, nil
}
func TestSuccessfulUpload(t *testing.T) {
// create temporary file, processUpload requires that it exists, even though
// we will not really be uploading it here
f, _ := os.CreateTemp("", "dautest-upload-*")
defer os.Remove(f.Name())
u := Upload{webhookURL: "https://127.0.0.1/", OriginalFilename: f.Name()}
u.Client = &MockClient{DoFunc: DoGoodUpload}
err := u.processUpload()
if err != nil {
t.Errorf("error occured: %s", err)
}
if u.Width != 640 || u.Height != 640 {
t.Error("dimensions wrong")
}
if u.Url != "https://cdn.discordapp.com/attachments/849615269706203171/851092588332449812/dau480457962.png" {
t.Error("URL wrong")
}
}
// func TestSuccessfulUpload(t *testing.T) {
// // create temporary file, processUpload requires that it exists, even though
// // we will not really be uploading it here
// f, _ := os.CreateTemp("", "dautest-upload-*")
// defer os.Remove(f.Name())
// u := Upload{webhookURL: "https://127.0.0.1/", Image: &image.Store{OriginalFilename: f.Name()}}
// u.Client = &MockClient{DoFunc: DoGoodUpload}
// err := u.processUpload()
// if err != nil {
// t.Errorf("error occured: %s", err)
// }
func TestTooBigUpload(t *testing.T) {
// create temporary file, processUpload requires that it exists, even though
// we will not really be uploading it here
f, _ := os.CreateTemp("", "dautest-upload-*")
defer os.Remove(f.Name())
u := Upload{webhookURL: "https://127.0.0.1/", OriginalFilename: f.Name()}
u.Client = &MockClient{DoFunc: DoTooBigUpload}
err := u.processUpload()
if err == nil {
t.Error("error did not occur?")
} else if err.Error() != "received 413 - file too large" {
t.Errorf("wrong error occurred: %s", err.Error())
}
if u.State != StateFailed {
t.Error("upload should have been marked failed")
}
}
// if u.Url != "https://cdn.discordapp.com/attachments/849615269706203171/851092588332449812/dau480457962.png" {
// t.Error("URL wrong")
// }
// }
// func TestTooBigUpload(t *testing.T) {
// // create temporary file, processUpload requires that it exists, even though
// // we will not really be uploading it here
// f, _ := os.CreateTemp("", "dautest-upload-*")
// defer os.Remove(f.Name())
// u := Upload{webhookURL: "https://127.0.0.1/", Image: &image.Store{OriginalFilename: f.Name()}}
// u.Client = &MockClient{DoFunc: DoTooBigUpload}
// err := u.processUpload()
// if err == nil {
// t.Error("error did not occur?")
// } else if err.Error() != "received 413 - file too large" {
// t.Errorf("wrong error occurred: %s", err.Error())
// }
// if u.State != StateFailed {
// t.Error("upload should have been marked failed")
// }
// }
func tempImageGt8Mb() {
// about 12Mb
width := 2000
height := 2000
upLeft := image.Point{0, 0}
lowRight := image.Point{width, height}
upLeft := i.Point{0, 0}
lowRight := i.Point{width, height}
img := image.NewRGBA(image.Rectangle{upLeft, lowRight})
img := i.NewRGBA(i.Rectangle{upLeft, lowRight})
// Colors are defined by Red, Green, Blue, Alpha uint8 values.

View File

@ -13,7 +13,7 @@ import (
"golang.org/x/mod/semver"
)
const CurrentVersion string = "v0.12.1"
const CurrentVersion string = "v0.13.0"
type GithubRelease struct {
HTMLURL string `json:"html_url"`

View File

@ -6,14 +6,14 @@ import (
func TestVersioningUpdate(t *testing.T) {
// pretend there is a new version
LatestVersion = "v0.13.0"
LatestVersion = "v0.13.9"
if !UpdateAvailable() {
t.Error("should be a version newer than " + CurrentVersion)
}
}
func TestVersioningNoUpdate(t *testing.T) {
// pretend there is a new version
// pretend there is not a new version
LatestVersion = "v0.12.1"
if UpdateAvailable() {
t.Error("should NOT be a version newer than " + CurrentVersion)

View File

@ -21,7 +21,7 @@
</p>
<p>The Watch Interval is how often new files will be discovered by your
watchers (configured below).</p>
watchers in seconds (watchers are configured below).</p>
<div class="form-row align-items-center">
<div class="col-sm-6 my-1">
@ -33,6 +33,17 @@
</div>
</div>
<div class="form-row align-items-center">
<div class="col-sm-6 my-1">
<span>Open browser on startup</span>
</div>
<div class="col-sm-6 my-1">
<label class="sr-only">Open browser</label>
<button type="button" @click="config.OpenBrowserOnStart = ! config.OpenBrowserOnStart" class="btn btn-success" x-text="config.OpenBrowserOnStart ? 'Enabled' : 'Disabled'"></button>
</div>
</div>
<div class="form-row align-items-center">
<div class="col-sm-6 my-1">
<span>Watch interval</span>

View File

@ -38,7 +38,7 @@
<thead>
<tr>
<th>filename</th>
<th>actions</th>
<th>state</th>
<th>&nbsp;</th>
</tr>
</thead>
@ -47,9 +47,11 @@
<template x-for="ul in uploads">
<tr>
<td x-text="ul.original_file"></td>
<td>
<button @click="start_upload(ul.id)" type="button" class="btn btn-primary">upload</button>
</td>
<td>
<span x-text="ul.state"></span>
<div x-if="ul.state_reason">(<span x-text="ul.state_reason"></span>)</div>
</td>
<td>
<img :src="'/rest/image/'+ul.id+'/thumb'">
</td>
@ -74,7 +76,10 @@
<template x-for="ul in finished">
<tr>
<td x-text="ul.original_file"></td>
<td x-text="ul.state"></td>
<td>
<span x-text="ul.state"></span>
<div x-if="ul.state_reason">(<span x-text="ul.state_reason"></span>)</div>
</td>
<td>
<img :src="'/rest/image/'+ul.id+'/thumb'">
</td>

View File

@ -19,7 +19,7 @@ import (
"github.com/gorilla/mux"
"github.com/tardisx/discord-auto-upload/config"
"github.com/tardisx/discord-auto-upload/imageprocess"
"github.com/tardisx/discord-auto-upload/image"
daulog "github.com/tardisx/discord-auto-upload/log"
"github.com/tardisx/discord-auto-upload/upload"
"github.com/tardisx/discord-auto-upload/version"
@ -133,7 +133,7 @@ func (ws *WebService) getLogs(w http.ResponseWriter, r *http.Request) {
func (ws *WebService) handleConfig(w http.ResponseWriter, r *http.Request) {
if r.Method == "POST" {
newConfig := config.ConfigV2{}
newConfig := config.ConfigV3{}
defer r.Body.Close()
b, err := ioutil.ReadAll(r.Body)
@ -178,7 +178,6 @@ func (ws *WebService) getUploads(w http.ResponseWriter, r *http.Request) {
func (ws *WebService) imageThumb(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "image/png")
processor := imageprocess.Processor{}
vars := mux.Vars(r)
id, err := strconv.ParseInt(vars["id"], 10, 32)
@ -192,7 +191,7 @@ func (ws *WebService) imageThumb(w http.ResponseWriter, r *http.Request) {
returnJSONError(w, "bad id")
return
}
err = processor.ThumbPNG(ul, "orig", w)
err = ul.Image.ThumbPNG(image.ThumbTypeOriginal, w)
if err != nil {
returnJSONError(w, "could not create thumb")
return
@ -202,7 +201,6 @@ func (ws *WebService) imageThumb(w http.ResponseWriter, r *http.Request) {
func (ws *WebService) imageMarkedupThumb(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "image/png")
processor := imageprocess.Processor{}
vars := mux.Vars(r)
id, err := strconv.ParseInt(vars["id"], 10, 32)
@ -216,7 +214,7 @@ func (ws *WebService) imageMarkedupThumb(w http.ResponseWriter, r *http.Request)
returnJSONError(w, "bad id")
return
}
err = processor.ThumbPNG(ul, "markedup", w)
err = ul.Image.ThumbPNG(image.ThumbTypeMarkedUp, w)
if err != nil {
returnJSONError(w, "could not create thumb")
return
@ -238,7 +236,7 @@ func (ws *WebService) image(w http.ResponseWriter, r *http.Request) {
return
}
img, err := os.Open(ul.OriginalFilename)
img, err := os.Open(ul.Image.OriginalFilename)
if err != nil {
returnJSONError(w, "could not open image file")
return
@ -276,7 +274,7 @@ func (ws *WebService) modifyUpload(w http.ResponseWriter, r *http.Request) {
return
} else if change == "skip" {
anUpload.State = upload.StateSkipped
anUpload.RemoveMarkupTempFile()
anUpload.Image.Cleanup()
res := StartUploadResponse{Success: true, Message: "upload skipped"}
resString, _ := json.Marshal(res)
w.Write(resString)
@ -298,7 +296,7 @@ func (ws *WebService) modifyUpload(w http.ResponseWriter, r *http.Request) {
}
// write to a temporary file
tempfile, err := ioutil.TempFile("", "dau_markup")
tempfile, err := ioutil.TempFile("", "dau_markup-*")
if err != nil {
log.Fatal(err)
}
@ -311,7 +309,7 @@ func (ws *WebService) modifyUpload(w http.ResponseWriter, r *http.Request) {
}
tempfile.Close()
anUpload.MarkedUpFilename = tempfile.Name()
anUpload.Image.ModifiedFilename = tempfile.Name()
} else {
returnJSONError(w, "bad change type")

View File

@ -78,7 +78,7 @@ func TestGetConfig(t *testing.T) {
t.Errorf("expected error to be nil got %v", err)
}
exp := `{"WatchInterval":10,"Version":2,"Port":9090,"Watchers":[{"WebHookURL":"https://webhook.url.here","Path":"/your/screenshot/dir/here","Username":"","NoWatermark":false,"HoldUploads":false,"Exclude":[]}]}`
exp := `{"WatchInterval":10,"Version":3,"Port":9090,"OpenBrowserOnStart":true,"Watchers":[{"WebHookURL":"https://webhook.url.here","Path":"/your/screenshot/dir/here","Username":"","NoWatermark":false,"HoldUploads":false,"Exclude":[]}]}`
if string(b) != exp {
t.Errorf("Got unexpected response\n%v\n%v", string(b), exp)
}