diff --git a/cmd/preview/preview.go b/cmd/preview/preview.go index a61a567..ef4ae9d 100644 --- a/cmd/preview/preview.go +++ b/cmd/preview/preview.go @@ -69,6 +69,20 @@ func update(w http.ResponseWriter, req *http.Request) { return } + width, height, err := readsize(original) + if err != nil { + httphelpers.ErrorResponse(w, err) + return + } + metadata, _ := client.GetPhotoMetadata(bucket, photo) + metadata.Width = width + metadata.Height = height + err = client.PutPhotoMetadata(bucket, photo, metadata) + if err != nil { + httphelpers.ErrorResponse(w, err) + return + } + resizes := make([]resizeOperation, len(previewOptions)) for i := range resizes { resizes[i] = resizeOperation{ @@ -138,6 +152,14 @@ func update(w http.ResponseWriter, req *http.Request) { w.Header().Add("X-Debug-Resize-Timings", fmt.Sprintf("%v", timings)) } +func readsize(buf []byte) (int, int, error) { + i, err := vips.NewImageFromBuffer(buf) + if err != nil { + return 0, 0, err + } + return i.Width(), i.Height(), nil +} + func resize(buf []byte, height int, format vips.ImageType, quality int) ([]byte, error) { i, err := vips.NewImageFromBuffer(buf) if err != nil { diff --git a/cmd/web/README.md b/cmd/web/README.md new file mode 100644 index 0000000..868f764 --- /dev/null +++ b/cmd/web/README.md @@ -0,0 +1,9 @@ + +# Creating embedded data + +```bash +go get -u github.com/go-bindata/go-bindata/v3/... +go generate +``` + + diff --git a/cmd/web/bindata.go b/cmd/web/bindata.go new file mode 100644 index 0000000..1fb54cd --- /dev/null +++ b/cmd/web/bindata.go @@ -0,0 +1,502 @@ +// Code generated by go-bindata. (@generated) DO NOT EDIT. + + //Package main generated by go-bindata.// sources: +// ../../web/shared/css/base.css +// ../../web/shared/css/enlarge.css +// ../../web/shared/css/gallery.css +// ../../web/shared/js/bleh.js +// ../../web/shared/js/enlarge.js +// ../../web/shared/js/gallery.js +// ../../web/shared/js/view.js +// ../../web/view/index.tmpl +package main + +import ( + "bytes" + "compress/gzip" + "fmt" + "net/http" + "io" + "io/ioutil" + "os" + "path/filepath" + "strings" + "time" +) + +func bindataRead(data []byte, name string) ([]byte, error) { + gz, err := gzip.NewReader(bytes.NewBuffer(data)) + if err != nil { + return nil, fmt.Errorf("read %q: %v", name, err) + } + + var buf bytes.Buffer + _, err = io.Copy(&buf, gz) + clErr := gz.Close() + + if err != nil { + return nil, fmt.Errorf("read %q: %v", name, err) + } + if clErr != nil { + return nil, err + } + + return buf.Bytes(), nil +} + +type asset struct { + bytes []byte + info os.FileInfo +} + +type bindataFileInfo struct { + name string + size int64 + mode os.FileMode + modTime time.Time +} + +// Name return file name +func (fi bindataFileInfo) Name() string { + return fi.name +} + +// Size return file size +func (fi bindataFileInfo) Size() int64 { + return fi.size +} + +// Mode return file mode +func (fi bindataFileInfo) Mode() os.FileMode { + return fi.mode +} + +// ModTime return file modify time +func (fi bindataFileInfo) ModTime() time.Time { + return fi.modTime +} + +// IsDir return file whether a directory +func (fi bindataFileInfo) IsDir() bool { + return fi.mode&os.ModeDir != 0 +} + +// Sys return file is sys mode +func (fi bindataFileInfo) Sys() interface{} { + return nil +} + + +type assetFile struct { + *bytes.Reader + name string + childInfos []os.FileInfo + childInfoOffset int +} + +type assetOperator struct{} + +// Open implement http.FileSystem interface +func (f *assetOperator) Open(name string) (http.File, error) { + var err error + if len(name) > 0 && name[0] == '/' { + name = name[1:] + } + content, err := Asset(name) + if err == nil { + return &assetFile{name: name, Reader: bytes.NewReader(content)}, nil + } + children, err := AssetDir(name) + if err == nil { + childInfos := make([]os.FileInfo, 0, len(children)) + for _, child := range children { + childPath := filepath.Join(name, child) + info, errInfo := AssetInfo(filepath.Join(name, child)) + if errInfo == nil { + childInfos = append(childInfos, info) + } else { + childInfos = append(childInfos, newDirFileInfo(childPath)) + } + } + return &assetFile{name: name, childInfos: childInfos}, nil + } else { + // If the error is not found, return an error that will + // result in a 404 error. Otherwise the server returns + // a 500 error for files not found. + if strings.Contains(err.Error(), "not found") { + return nil, os.ErrNotExist + } + return nil, err + } +} + +// Close no need do anything +func (f *assetFile) Close() error { + return nil +} + +// Readdir read dir's children file info +func (f *assetFile) Readdir(count int) ([]os.FileInfo, error) { + if len(f.childInfos) == 0 { + return nil, os.ErrNotExist + } + if count <= 0 { + return f.childInfos, nil + } + if f.childInfoOffset+count > len(f.childInfos) { + count = len(f.childInfos) - f.childInfoOffset + } + offset := f.childInfoOffset + f.childInfoOffset += count + return f.childInfos[offset : offset+count], nil +} + +// Stat read file info from asset item +func (f *assetFile) Stat() (os.FileInfo, error) { + if len(f.childInfos) != 0 { + return newDirFileInfo(f.name), nil + } + return AssetInfo(f.name) +} + +// newDirFileInfo return default dir file info +func newDirFileInfo(name string) os.FileInfo { + return &bindataFileInfo{ + name: name, + size: 0, + mode: os.FileMode(2147484068), // equal os.FileMode(0644)|os.ModeDir + modTime: time.Time{}} +} + +// AssetFile return a http.FileSystem instance that data backend by asset +func AssetFile() http.FileSystem { + return &assetOperator{} +} + +var _sharedCssBaseCss = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x74\x93\xc9\x72\xa3\x48\x10\x86\xcf\xe2\x29\xea\x34\x5a\x42\x85\x00\x5b\x96\xa7\x08\xc7\xf8\x34\xa7\x99\x98\xcb\xf4\x03\x94\x20\x81\xb4\x6b\x8b\xaa\x44\x12\xea\xe8\x77\xef\x60\x91\x5a\xb6\xdc\x37\x82\x5c\xfe\x2f\x33\xff\x7a\x45\xed\xac\x27\xd6\x7a\xb5\x98\x37\x44\x2e\x88\xcd\xa6\xb2\x86\x42\x5c\x5b\x5b\x2b\x90\x0e\x43\x5c\x58\xbd\x29\x42\xc8\xfe\xaa\xa4\x46\xd5\xbd\xfc\x2d\xdb\x40\x68\xa4\x38\xd6\x0d\xbd\x3e\x26\x49\xbe\xdd\x26\x7f\x94\x18\x9c\x92\xdd\x4b\x38\x4a\x37\x5f\xe6\x51\xb4\x59\x45\x6c\xc5\xbe\x19\xa4\x10\xb1\xd5\x26\x8a\x84\xb7\x96\xd8\xf7\x68\xc6\x79\x70\xb2\x40\x53\x0b\x96\xc4\x0f\xbb\xad\x07\x9d\x47\x3f\x2e\x15\xff\x77\xce\xd6\x5e\xba\xa6\x1b\xcb\x1a\xd2\xaa\xaf\xea\xb9\xf8\x88\x20\xd8\xfc\x02\x31\x5f\xb3\x00\x1e\xab\x7c\x4a\x38\x02\xd6\x0d\x09\xd6\x63\x45\x33\x82\x13\x71\x0f\xa6\x04\x3f\xc8\x59\x47\xa8\xf1\x0c\xff\x40\x8d\x7b\x54\x48\xdd\x20\xbc\xb7\x65\xd7\x4b\x68\xe9\x6b\x34\x82\x25\x03\xd1\xd4\x31\xe0\x19\x04\x4b\x41\xe7\x6c\xb3\x62\xe9\x93\xa3\x1e\x6b\xa6\xd0\x00\x6f\x26\xb5\x34\xce\x76\xd9\xd0\xaa\x49\xd7\xac\xc9\xd6\xac\x79\x58\xb3\xe6\x71\xcd\x9a\xed\x9a\x35\x4f\x57\xfe\x0b\xde\x76\x9b\xf4\x02\xfc\x08\xfb\x77\x24\x3e\x0a\x69\x6b\xa9\x19\x38\xa5\x21\x94\x0a\x65\x80\x32\x8f\x66\x5c\xdb\x33\xb7\xe1\x74\x97\x56\x7b\xe8\x42\x21\x15\x4c\xd2\x57\x99\x91\x39\x8b\x93\xed\x73\x3a\x2d\xb7\xc9\x3e\x45\xd3\xf8\x29\x7d\xbe\x04\x1f\xee\x82\xd9\x2e\xbb\x04\xef\xc7\xb8\x4f\x1a\x6f\xf7\xaf\x55\x50\xb4\x0a\xa6\x8b\xc7\xce\x83\xb2\xb2\x84\x92\x53\xd3\xea\xbd\x91\x38\x9c\x72\xf2\x8a\x60\x7b\x65\x8b\xf7\x3c\x9a\x39\x1b\x90\xd0\x1a\xc1\x3c\x28\x49\x78\x18\x26\xfa\xb2\xdc\x61\x41\xad\x07\x61\x2c\x2d\xbe\x4a\xe0\xa8\x65\x0d\xcb\x5e\xe6\x57\x57\xb9\x0f\x56\xb5\x04\xbd\x25\xac\x13\xac\xf7\x86\x82\x8a\xc6\xaf\x33\x47\x53\xc2\x49\xb0\xf4\x77\xaa\x63\x53\x86\xba\xee\xfb\xda\xfd\x1b\x14\xc4\x2b\x24\xc1\x0a\x7b\x00\x7f\xb3\x81\xff\x7c\x2d\x0d\x06\x3d\x6d\xc0\xc8\x03\x6b\x87\x99\x9d\x2c\xcb\xe1\x68\x07\xe9\x17\xd7\x17\xb0\xcc\x6f\x5c\xd7\x3b\x42\x61\x20\x1e\xa8\x53\x20\x98\xb1\xa6\x27\xbe\x6e\xab\x52\x70\xca\xa3\xd9\x5b\xef\xfc\xaa\xe3\x85\x35\x04\x86\xc6\xff\x1c\x4c\x39\x60\x4c\x8a\x0a\x6f\x0d\xfd\x59\xf3\xfe\x04\x1f\x0e\x9b\xc4\x7f\xf6\x67\x9d\x29\x20\x02\x7f\xfb\x5a\x93\xdd\x10\x18\xde\x15\x79\x69\x42\x65\xbd\x16\xac\x75\x0e\x7c\x21\xc3\x64\x44\x90\x25\xf8\x0f\x43\xa7\x1e\x34\x2b\xa4\x2a\x16\x1f\x51\xd8\x8a\x65\xcb\xa1\xe8\x67\x00\x00\x00\xff\xff\x8a\xe3\xf6\x01\x8c\x04\x00\x00") + +func sharedCssBaseCssBytes() ([]byte, error) { + return bindataRead( + _sharedCssBaseCss, + "shared/css/base.css", + ) +} + +func sharedCssBaseCss() (*asset, error) { + bytes, err := sharedCssBaseCssBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "shared/css/base.css", size: 1164, mode: os.FileMode(420), modTime: time.Unix(1590429739, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + +var _sharedCssEnlargeCss = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xac\x92\x6f\x8b\xdb\x30\x0c\xc6\x5f\xc7\x9f\x42\x6f\x0e\xda\x51\x97\xec\xa0\x8c\x8b\x3f\x8d\x6b\x2b\x89\x38\xd7\x32\xb6\xfa\xe7\x3a\xf6\xdd\x87\xd3\x76\x47\xda\xb1\x75\x30\x08\x21\x52\xe0\x79\xf4\x7b\xa4\x35\xc6\x60\xf3\x80\xf0\x5d\x35\x89\x0b\x09\x71\xec\xa0\xa7\x13\x7a\xa3\x1a\xe1\xd4\x41\x6b\x54\x13\xb0\x97\xcb\xd7\x91\xbc\x8c\x1d\x7c\x6d\xdb\xc3\xd1\xa8\x66\x44\x1a\x46\xb9\xd4\xa3\x51\xcd\x59\x53\xf4\x78\x9a\x1a\x46\x35\x7c\xc0\xdc\x07\x3e\x76\x30\x92\xf7\x18\x8d\x52\x8d\x64\x1b\x6f\x46\xed\x7a\x53\x80\x93\x75\x24\x1f\x46\x35\x5b\xeb\xde\x87\xcc\xfb\xe8\x3b\xc8\xc3\xd6\x2e\xda\x15\x5c\x9f\xf5\xdb\x66\x69\xd4\x0f\xa5\x6e\x13\x6b\x17\xb8\xe0\x0a\x7e\xd5\x67\xe6\x9d\x76\x1c\x25\x73\x28\x95\xa7\xe7\x28\xba\xd0\x19\xab\xcf\x1b\xee\x26\x0e\x11\xcc\xba\x54\xc7\x38\xd4\x7e\xfb\x6d\xfa\x21\x78\x12\x3d\x4d\xd6\x73\xde\x75\xb0\x4f\x09\xb3\xb3\x05\x7f\xe3\x39\x65\x65\xbd\x9f\x14\x9c\x0d\x6e\x71\xb0\x79\xa1\x6f\xaa\x4b\xf8\x02\xaf\xcb\x4a\xfa\x87\x40\xf3\x25\xb6\xf6\x31\x32\xc7\x81\xf3\x15\xff\x75\xb3\x59\xc1\xe7\xab\x5d\xdf\x67\xf0\xc0\xfc\xd4\x0e\xe7\x8e\x0f\x7a\x14\x57\x77\x1d\xde\xcb\xd3\xd0\x9e\x4a\x0a\xf6\xa3\x03\x8a\x81\x22\xea\x6d\x60\xf7\xfe\xaf\x5c\x15\x09\xa3\xdc\x21\x65\x0c\x56\xe8\x80\xe6\x7a\x44\x75\x55\x9a\x33\x0d\x14\x3b\x10\x4e\x50\x11\xe7\x42\x69\x64\xe1\xb9\x8a\xdd\x16\x0e\x7b\xc1\xbf\xdd\xf7\xcb\xfc\xbc\x5f\xaa\xf0\xa7\x6e\xc6\xc0\xd6\xff\x3f\xe5\x9f\x01\x00\x00\xff\xff\xc3\x89\x55\x44\x89\x03\x00\x00") + +func sharedCssEnlargeCssBytes() ([]byte, error) { + return bindataRead( + _sharedCssEnlargeCss, + "shared/css/enlarge.css", + ) +} + +func sharedCssEnlargeCss() (*asset, error) { + bytes, err := sharedCssEnlargeCssBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "shared/css/enlarge.css", size: 905, mode: os.FileMode(420), modTime: time.Unix(1590429739, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + +var _sharedCssGalleryCss = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x94\x91\xcd\x6e\xea\x30\x10\x85\xd7\xf1\x53\xcc\x92\xa0\x6b\x08\xdc\xcb\xad\x9a\x6c\xfa\x2a\x13\x7b\x08\x53\x9c\xd8\xb2\xcd\x5f\x2b\xde\xbd\x72\x09\x6d\x40\xa0\xd2\x8d\x2d\x4b\x67\xbe\x33\x3e\xa7\xf4\xd6\x46\x78\x17\x99\x94\x0d\x1a\x43\xfe\x20\x83\x43\xc5\x5d\x53\xc2\x16\xfd\x48\x9e\x9f\x79\x35\xd4\xd4\x18\x48\xae\x88\x9b\x55\x2c\x61\xfe\xaf\x70\xfb\x4a\x1c\x85\x78\x69\x49\x33\x42\x50\x9e\xa8\x03\xec\x34\x8c\x5a\xdc\xcb\x1d\xeb\xb8\x2a\xe1\xb9\x28\xdc\x3e\x4f\x66\x5f\xae\x77\x91\xc5\x27\x32\x3b\xfe\x4c\x7d\x7a\x9c\x3a\xfb\xff\x30\x75\xf1\x0b\xea\x7c\x40\x9d\xf4\x92\x34\xe0\x50\xeb\x41\x90\x57\xf9\xa6\x40\x7b\x85\xf4\x27\x52\x51\x09\x91\x69\x0e\xce\xe0\xa1\x84\xa5\xa1\x84\x4d\x97\xdc\x79\x74\x25\xa4\xb3\x1a\xba\x48\x8e\xd4\x26\xab\x16\x7d\xc3\xdd\x99\x23\x8b\xc9\xa2\x6f\xe4\x52\xe9\x58\xc5\x8d\xa7\xef\x89\xfb\xbb\x0d\x16\xa9\x8d\x55\xeb\x1b\x34\x6e\x9b\x44\xba\xd6\x65\xe7\x60\x14\x1a\x35\xba\xe4\x0f\x82\xcb\x61\x0c\xb3\xc9\x3c\xaf\x60\x3a\x86\xbf\x29\x43\xe0\x00\x1d\x46\xde\x12\x04\x7e\x23\xb0\x1d\x68\x0a\xeb\x68\xdd\x9f\x53\x75\x37\x04\xad\xad\xd9\x10\x8c\xa7\x22\xeb\x9b\xc3\x4d\xb4\x95\xc8\x6c\xfd\x4a\x2a\xca\x25\xa7\x45\xec\x96\x7c\xfa\xc0\x47\x00\x00\x00\xff\xff\x00\x1d\x81\x7b\xee\x02\x00\x00") + +func sharedCssGalleryCssBytes() ([]byte, error) { + return bindataRead( + _sharedCssGalleryCss, + "shared/css/gallery.css", + ) +} + +func sharedCssGalleryCss() (*asset, error) { + bytes, err := sharedCssGalleryCssBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "shared/css/gallery.css", size: 750, mode: os.FileMode(420), modTime: time.Unix(1590429739, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + +var _sharedJsBlehJs = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x14\xc7\x31\x0e\x85\x20\x0c\x00\xd0\x9d\x53\xb0\xb1\xb5\xe1\x0f\x7f\xf8\x7f\xf1\x14\x1e\x40\xa9\x11\x62\x81\xb4\x05\xaf\x6f\x1c\x5f\xe6\xde\xc4\xfc\x3a\xc8\x1f\xd2\xd8\x87\xd3\xac\xeb\x0f\x71\x4f\x15\x8a\x26\xba\xf2\x14\xa8\x64\x58\x3b\xe3\x1c\xb4\x7c\xe0\x0b\x31\x62\xca\x6a\xaf\x81\x94\x61\x93\x76\x2b\x09\x14\x0d\x7f\xe7\x9e\x00\x00\x00\xff\xff\x84\x23\x96\x39\x54\x00\x00\x00") + +func sharedJsBlehJsBytes() ([]byte, error) { + return bindataRead( + _sharedJsBlehJs, + "shared/js/bleh.js", + ) +} + +func sharedJsBlehJs() (*asset, error) { + bytes, err := sharedJsBlehJsBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "shared/js/bleh.js", size: 84, mode: os.FileMode(420), modTime: time.Unix(1590429739, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + +var _sharedJsEnlargeJs = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xcc\x59\xdd\x6f\xe3\xb8\x11\x7f\xb6\xff\x8a\x29\xae\x80\xe4\xc4\xb1\x9d\x2d\xf6\x65\x53\x2f\x70\xc8\x06\xb8\x02\x2d\x5a\x6c\xb6\xd8\xdb\x2b\x8a\x0d\x23\x8d\x2d\xf6\x68\x52\x20\xa9\xd8\xde\x85\xff\xf7\x82\x1f\x12\x29\x99\x4e\x9c\x16\x07\xdc\x93\x2d\x72\x3e\x7e\xf3\xc1\x99\xa1\x84\xbb\x5a\x48\x0d\x25\xae\x48\xc3\x34\x14\x8c\x28\x05\x77\x9c\x11\xb9\x46\xf8\x3e\x1e\xcd\x2f\xc6\xa3\x5a\xe2\x8a\xee\x60\x09\xd9\x0f\x4f\x14\xb7\x57\xd9\xcd\x78\x84\x0c\x61\x09\xbc\x61\xec\x66\x3c\x2a\x98\x50\x78\xd7\x5b\x11\x5c\x23\xd7\xaa\xb7\x58\x57\x42\x8b\xfe\x8a\x44\x26\x48\x19\xaf\x8d\x47\xdf\x84\xd8\xc0\x12\x16\x37\xe3\xd1\xce\xff\xee\xdd\xef\x78\xb4\x11\x8d\xc2\x0f\x62\xcb\x61\x09\x2b\xc2\x14\x9a\xc5\x5a\xa2\x42\xf9\x84\xe5\x7d\x21\x05\x63\xb0\x84\xef\x87\x9e\x70\xe5\x97\xc6\x23\x51\x23\xbf\x35\x68\x3f\xd1\x0d\x8a\x46\x07\x28\x17\xf3\xb1\x85\xad\xb4\x6c\x0a\x2d\x64\x8e\x0c\xa7\xe0\x4c\x5f\xb6\x86\x4f\x8c\x4b\x46\xba\xa2\x6a\xd6\x39\xc5\xfd\xb9\x69\xd7\x23\xf4\x6e\x61\xd7\x7b\xda\xf7\x9e\x12\xe6\x04\xf1\x29\x9b\xba\xcd\x23\xcb\xdc\x4e\x67\xc0\x67\x49\xea\x1a\x65\x3e\xe9\x98\x1e\x29\x2f\x7f\x11\x62\x73\x2b\xb8\x96\x82\x29\xb3\x15\xef\xdd\xd3\x6f\x38\x20\xff\x4c\x79\x29\xb6\x43\xc2\x4f\xa2\x29\xaa\x01\xe5\x07\x49\xd6\x83\x25\x07\xdc\x33\x23\xc3\x99\x01\xc4\xcb\xdb\x8a\xb2\x32\xb7\x54\xc8\xd0\xb0\x1c\x62\xc7\x77\xb8\x83\xa7\x5d\xaa\x95\xa2\x68\x36\xc8\xf5\xac\x90\x48\xb4\xc9\x36\xf3\x94\x67\x25\x7d\xca\x82\x62\xa3\xc7\xe6\xf0\x5f\xa9\xd2\x33\x52\x96\x79\x86\x2e\x99\x07\x44\x4a\xef\x19\xce\x4a\xaa\x6a\x46\x4c\x4c\x32\x2e\x38\x66\x09\x12\x51\x93\x82\x6a\x4b\xb2\xc8\x82\x23\xbe\x45\x9e\xbc\x7b\x05\xc0\x01\xdf\x09\xb0\x57\x86\xec\xaa\xf0\x74\x03\xe8\x47\x7e\x1c\xc8\xb4\xd4\xb1\xbe\xbf\xf0\xe7\x11\x92\x01\x3e\x4b\xff\x2c\x32\xca\x93\x2c\x94\x73\x94\x9f\x70\x67\x8e\x55\x66\x72\x0d\x2e\xb3\x04\x59\x25\x71\x65\xab\x49\x76\xd2\x2d\x49\x1b\x2d\xf7\xa4\x1f\x83\xbf\x37\xfa\x75\xc6\x39\x86\x67\xad\x13\x8d\x4e\x33\x1d\xdb\x77\x95\xa5\xe8\xfe\x57\x03\x1d\x7b\x64\x61\x54\x58\xcf\xb1\xaf\x25\x3f\x65\x9d\xdd\x4f\xd1\xf7\x0c\xb3\xf5\x31\x3b\x26\x4a\x58\x95\x4c\xc7\x96\x61\xd2\x2b\x4b\x51\x3b\x38\xef\xa4\x44\x3c\x27\xed\xf1\x24\x2f\x1d\x90\x48\x54\x04\xaa\xd7\x7c\x4e\x61\xa2\x9b\x75\x24\x3d\xb0\x9c\x82\xe4\x29\xd2\x3c\x4a\x16\xc6\x81\x59\xd2\xc6\x23\xd4\x81\x31\x06\x1d\x7a\xe8\x99\x90\x3d\xc3\x49\xc0\x66\x3f\x45\xff\x4a\xb0\x9e\xad\x2d\xe8\xc7\xdd\x26\x14\xf4\x50\x09\x48\x59\xde\x3d\x21\xd7\x06\x16\x72\x94\x79\x56\x30\x5a\xfc\x9a\x4d\x21\xc7\x09\x2c\xdf\x5b\x9e\x11\x1a\x57\x18\xb2\x0f\x6e\x4a\x71\x8d\x26\xc8\xfa\x51\x87\x23\x04\x17\x70\xb9\x98\xbd\x79\x3b\x05\xbb\x64\xda\xb6\x19\x70\x4c\x73\x9b\x6d\x61\x0e\x6f\x52\x1b\x95\xd9\x88\x84\x96\x92\x6c\x9d\x92\xc3\xd4\xf5\xe5\x64\x39\xf8\x4d\xd0\x5f\xfd\x46\xe8\xdb\xa0\xb8\x36\xdf\x9b\x63\xda\xf4\x3c\xb6\xc7\xa6\xb2\xd7\xd9\xd4\x25\xd1\x68\x35\x1a\x39\x16\xf4\xe4\xc8\x3d\x5d\xfa\xfc\x7f\xc2\x5a\xb4\xed\x04\x62\xf1\x6e\xed\x43\x42\xb2\x44\x45\xbf\xe1\x6b\x65\xfb\x39\x06\xbe\x8f\xc3\x9a\x9b\x63\x7a\xb3\x47\x42\x5f\x2d\x28\xd7\x28\x4b\xb1\xe5\x83\x60\x1f\x4d\x75\x5a\x36\x18\x42\x13\x1f\x21\x37\x5e\x14\x8d\x54\x42\x9a\x83\xb6\x96\xe4\xf1\x91\xf2\x75\x96\xce\xbb\x67\x91\x6c\xc4\x13\x9e\x97\x76\x74\x05\x79\x1f\xa4\xb3\xb6\x0d\x1f\xe1\x39\xce\x8c\x38\x53\x4b\x7e\x9e\x42\x78\xf8\xe2\x04\x0c\x73\x6c\x74\x78\x3d\xdc\xa6\x7e\xc9\x6d\xdd\x30\x7c\x9e\xdf\x8e\x7c\xd6\x65\xbb\x1f\x42\x5f\x8a\xe8\xb6\x42\x64\xe7\x79\x70\x3e\x87\x1f\x95\x6a\x36\x08\xab\x86\x31\x50\x85\x44\xe4\xc3\x13\x8d\xb3\x12\x99\x26\x5f\xdc\x81\x5e\x2c\xde\xc0\x05\x74\xfb\xc6\xab\x05\xa3\xad\x83\xdd\xdf\x2f\x67\x9d\x60\x73\x7f\xf9\x89\xa8\xca\x5b\x64\xc7\x66\xa8\x88\xaa\x60\x09\xfe\x78\x30\x51\x10\x4d\x05\x9f\x99\x65\x23\xc3\x84\xfc\x0f\xe6\x61\xa6\x34\x91\x5a\x7d\xa6\xba\xca\xa3\x3b\xcc\x64\x12\x05\xc1\x36\x6f\x6f\xa9\x44\xdd\x48\x6e\x61\x74\xba\x68\x09\x4b\xab\x70\x26\xb1\x66\xa4\xc0\x58\xd2\x14\xb2\xa8\x93\x18\xac\x39\x2d\x5b\xe8\x5b\xa2\x8b\x2a\xc2\x7e\xf2\x34\x1b\xe9\x45\x45\xf8\xba\x3b\xd1\xad\xd5\xcf\x15\x9e\xe0\x19\xaf\x4f\xa1\xfe\x87\xab\x6d\x06\x84\x55\x39\x9f\x43\xa5\x75\xad\xde\xcd\xe7\x05\xe1\xb4\x51\x38\x2b\xc4\x66\xfe\xc3\x0a\x89\x5e\x6e\x4a\x7e\xf5\x1f\xf2\x44\x54\x21\x69\xad\xbf\x8a\x1a\x25\xd1\x42\xaa\xaf\xa2\x36\xee\x24\xec\x6b\x51\x11\xca\x29\x5f\x8f\x47\x23\x86\x1a\x7c\xe5\x0c\x57\xc8\x70\xb8\xa2\x0b\xda\xbf\x68\xf9\x6f\xef\xe0\xc0\x90\x22\x32\x29\x2d\x91\xeb\x7b\x59\x38\x97\x7b\x81\x9e\x2c\x0e\xd2\xd1\x48\xe1\x17\x6e\x52\x14\xd1\x1d\xe7\x91\x89\xe2\x57\x77\x56\x00\x99\xc2\xe7\x44\xba\xc6\xff\x9c\xb4\xee\xc6\x74\x08\x0e\x37\xd5\xbf\x73\xf7\xab\xee\xc3\xc6\x54\xda\xb7\xb2\x3f\x89\xd0\x32\x0d\x3c\x35\xaf\x78\x44\xf3\x39\xfc\xd3\x36\x03\xd0\x15\x82\xe9\x11\x20\x56\xf6\x3f\xdd\x90\x35\x8e\x47\xa1\x55\xc4\x25\xe2\xb8\xda\x6c\x69\xa9\xcd\x11\x7b\xf8\xe3\x77\x4b\xa1\x6c\x43\x3e\xd4\xbb\x87\xe4\x74\xe4\x98\x2a\xa4\xeb\x4a\x0f\xb9\xaa\x96\xcb\x9c\x27\x61\x3c\x8a\x8f\xcd\x3a\x7f\x70\x50\x4a\x87\x52\x0b\xe8\x6b\x9a\x42\x5f\xc6\x43\x48\xfc\xae\x58\x18\x8b\xdd\xc3\x91\xf7\xfd\x0b\x1d\x33\x03\x6e\x6a\x33\x96\xe5\x51\x31\x72\x37\x76\xa2\xf0\xbe\x20\x2c\x3a\x51\xfb\x21\xa3\x63\xda\x27\x67\x10\xbf\xe8\xe0\xc5\xc5\xae\xfd\x9f\x50\xb0\x4b\x2b\xd8\x25\xa7\x9f\x58\xc1\xf6\x4c\x05\xc7\x31\xd1\x92\x70\xb5\x12\xd2\x38\xe5\xc1\x3e\x30\xa2\xf1\x4f\x65\xee\xdd\xbb\x3b\xd4\xbb\xce\xd7\x7b\xfb\xb0\x98\x80\x32\x62\x5b\x92\xa4\xce\xc3\xe4\xc1\x47\xc0\x74\xd0\xdd\x14\xf6\x51\x10\x76\x70\xb9\x84\x5d\xe4\xd7\xcb\x25\xec\x3d\xb9\xef\x17\x8f\xfb\x29\x04\x2e\x57\x6a\x39\x6e\x7f\x79\x31\x7c\x70\x09\x8f\xfb\x64\x0c\xe7\x73\xb8\xb5\x55\x14\xa4\xe9\x07\x9d\x58\x57\x5a\xcb\x8f\x66\xd1\xd4\x2e\xaf\x65\x1e\x3c\xea\xb9\x3f\xa2\xb5\x1b\x70\x47\x95\xa6\x7c\x0d\x62\xb5\x52\xa8\x83\x55\x17\xcb\x9e\xb0\xc8\xc0\xc4\x4e\x24\xd0\xf7\xee\xa1\xb8\xab\x25\xe4\x3d\x70\x57\x70\x3d\x81\x8b\x9e\xe7\x4e\xd1\xec\xbd\x0a\x7f\xdc\x39\x6e\xc1\x58\x32\x38\x06\xde\xd6\xb6\x41\x68\xa2\x69\x01\x3e\xf3\x7c\x2e\x50\xc1\xa7\xd0\x66\xde\xd4\x9e\x46\x17\x13\x53\x9f\xda\x75\x78\x1f\x6d\x18\xb5\x77\x5c\x35\x12\xa1\x40\xae\x25\x96\xa1\x77\x46\x2c\x57\x9e\x65\x0e\x6f\xda\x8e\x6a\xdb\x45\xd0\x0b\xef\x4d\xae\xb5\x22\xef\x4d\xaf\x06\x45\x4b\x8c\xc4\x2d\xa2\x12\x38\xe4\xfe\xf3\xb1\xb2\x18\x60\x39\x94\x35\x20\x1e\x16\x57\x4f\x15\x69\x88\x4b\x7d\xe4\x3a\x9b\x8f\xae\x92\x84\x04\x6c\xdb\xed\x2d\xa9\xa1\xa2\xeb\x0a\x24\x2a\xc1\x1a\x0b\xd4\x25\x01\xd1\x70\x3d\x7b\x1b\xa8\x98\xd8\xc6\x44\x36\x62\x1d\x8d\xcb\xdc\x0d\xd9\xf9\x03\xf1\x37\xa2\xab\xd9\x86\xec\xf2\xeb\xd9\x5b\x98\x07\xbd\x53\x43\x6f\xb3\xdf\xc3\x77\x84\x94\xe7\x1d\x87\x43\x7a\x3d\x99\xb6\xe2\xda\xd2\xb9\x46\x1d\x04\xe5\x21\xe8\x36\x81\x88\xaa\xb1\xd0\x36\xe5\x66\x44\xc2\xfb\x13\xf7\xb3\x44\x61\x0c\x31\xb0\xcd\x11\xa8\x72\x6e\xc3\xd2\x54\x79\xdb\x58\x62\x77\x9f\x16\xeb\x2a\xdf\x30\x4c\x27\xe4\xba\xde\xf3\xac\xe0\xaa\x27\xb8\x8a\xa2\x6b\x3c\x11\x93\x7a\x67\x78\x49\x56\xed\xf6\x1d\x74\xd3\xb4\x3b\xc7\x9f\x8d\x25\x53\xb3\x57\x1d\xed\xfd\x64\xd1\x98\xcd\x43\xe4\xec\xc8\xa7\x09\x0d\x44\xbe\xeb\x55\xfc\x1e\xd8\xa1\x28\x15\x50\x86\x19\xac\x1d\x0c\x38\xd1\x8d\x24\xcc\x02\x8c\xce\x58\xac\xac\xb3\x27\xc9\x64\xad\xea\xcc\x1a\x92\x04\xe3\x2c\xa4\xd4\x21\x1a\xe8\x08\xd3\xd4\x33\x5a\x8e\x88\x8e\xf4\x84\x1b\x41\x7f\xe2\xea\x0f\xbe\x37\xf1\x72\x3b\x9e\x85\xf7\x49\xaa\x12\x61\x7a\xf0\xc3\x7f\x52\x94\x99\x71\x13\xc2\xfc\x72\xbb\x5e\xd1\x12\xc3\x04\x5e\x75\x57\x77\x77\xc8\xb5\x29\x92\xca\x7d\xd0\xa8\x85\xa2\xe6\xa4\x9f\xf8\xe0\x61\x87\x03\x7f\x45\x70\x1c\x3f\x9f\xfa\x36\x62\x07\x95\x1e\xe9\x97\x00\x28\xf9\xae\x3f\xcc\xc1\x03\x1a\x7f\x3f\xb5\x17\x12\x65\x28\x09\x63\x96\xae\x7b\xd7\xf6\x28\xca\x7d\xfb\x65\xe0\x09\xe5\xca\x94\xad\x25\x64\x15\x2d\x4b\xe4\xee\xfb\x40\x97\x81\xc3\x8f\x4d\x3e\xf1\x0a\x86\x44\xfa\xa5\x13\x84\x6d\x87\x48\xee\xc2\x12\x14\xea\x56\x40\x3e\xb8\x45\x27\xbf\x5c\x5c\xcf\x16\xed\x15\xf9\x7a\xb1\x68\xe3\xe3\x82\xd5\xc6\xe7\x23\x2a\x2d\x92\xf1\xe9\xf9\xf6\x93\xc8\xd3\xf1\x9a\x42\x3a\x38\x93\x54\x30\x06\x5f\x55\x5e\x0e\x44\x77\xd9\x78\x29\x12\x4f\x54\xd1\x47\x86\xbf\xaf\x50\x9c\xcc\xab\x73\x3e\x48\xb9\xa0\x75\x51\x93\xb8\x36\x57\x65\x99\xd3\x72\x0a\x18\xf5\xda\x7b\xba\xa9\x19\x42\xdb\x42\xed\x22\x86\xf7\xf6\xd1\x3d\x1d\x2e\xfd\x6d\xca\x70\xe1\x86\x70\xd3\xcc\x7d\xe0\xdc\x30\x11\x09\x49\xbf\x30\x79\xc5\x9b\xce\xd4\x7b\x89\x13\x78\x86\xaf\x3b\x5a\x5b\x43\x3d\x8b\x4c\x4e\x5d\xa2\x61\x69\xf6\x2d\xf7\x61\xfc\xdf\x00\x00\x00\xff\xff\x43\x9a\xed\xd3\xdc\x1e\x00\x00") + +func sharedJsEnlargeJsBytes() ([]byte, error) { + return bindataRead( + _sharedJsEnlargeJs, + "shared/js/enlarge.js", + ) +} + +func sharedJsEnlargeJs() (*asset, error) { + bytes, err := sharedJsEnlargeJsBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "shared/js/enlarge.js", size: 7900, mode: os.FileMode(420), modTime: time.Unix(1590429739, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + +var _sharedJsGalleryJs = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xb4\x57\xdd\x6e\xdb\xb8\x12\xbe\x96\x9e\x62\x0e\x50\xc0\x52\x62\xcb\x49\xce\xb9\x8a\xeb\x02\xa7\xdd\xa0\x0d\xd0\x45\x17\x9b\xb6\x7b\x51\x14\x08\x23\x8d\x25\x62\x29\x52\x4b\x52\xb1\xdd\xd4\xef\xbe\xe0\x8f\x24\x4a\x76\x8a\x62\x81\xbd\x8a\x33\x1c\xce\xcc\x37\xfc\xe6\x47\x39\x23\x4a\xc1\x7b\xb2\x17\xad\xbe\xe1\x25\xe5\x08\x4f\x71\xb4\x3c\x8b\x23\x89\xb9\x56\xb0\x86\x2f\x5f\x57\x71\x1c\x95\xa4\x81\x35\x5c\x5e\xad\xe2\xe8\x81\x28\x7c\x87\xb4\xac\xb4\x91\x5c\x5c\xac\xe2\x68\xb9\x84\x9a\xec\x7a\xe1\x7f\xaf\x8c\xf0\x91\xe2\xb6\x11\x52\xff\x41\x0b\x5d\x59\xd5\xab\xff\xad\xe2\xe8\x6c\x19\x47\x71\x94\x0b\xae\xb4\x6c\x73\x2d\x64\x92\x1a\x97\x91\xae\xa8\xca\x42\xa7\x5e\x14\x78\x76\x82\x13\xfe\xdd\xc1\x33\x0e\x0f\xb1\x0d\xf0\x77\x2c\xa9\xd2\x28\x81\x00\xc7\x2d\x18\x4f\x84\x97\x0c\x61\x4b\x75\x05\x84\x03\x51\x0d\xe6\x1a\x24\xd1\x54\x80\xd8\x00\x91\x40\x34\x50\x5e\xe0\x2e\x8e\x28\x57\x28\x75\x42\xe4\xfa\x72\xee\x64\xeb\xc5\xa5\x0b\xdc\x42\xb1\xf6\x60\x6d\x05\x11\x91\xd7\x40\xe4\x3c\x8e\xa2\x83\x09\x8e\x6e\x20\xb1\x57\x60\xbd\x86\xee\x56\x80\x37\x6b\x5a\x55\x25\xe6\x67\x6a\xd4\x0f\x80\x4c\xe1\x91\x92\x6a\x18\xcd\xd1\x19\x9a\xc3\xc5\x1c\x86\x0b\x3d\xc6\x4f\x5c\x3e\x87\x72\x80\xd2\x88\x26\x19\x23\x58\x2e\xe1\xe3\x87\x5f\x3e\xf4\x66\xde\x22\x47\x49\x34\x02\x01\x46\x95\x36\xc9\x28\x68\x8d\x5c\x51\xc1\x15\x6c\x84\x04\x24\x79\x35\x18\x8f\xa3\x9c\xb0\xbc\x65\x44\xa3\x7f\x4c\x86\x3a\xbc\xd2\xb1\xc8\xca\xf3\x56\x4a\xe4\xfd\x2b\x75\x8f\xbc\x1a\x1f\xdf\xba\x84\xc1\x45\x7f\x8f\x11\xa5\xfb\x77\x9f\x32\xe1\x0c\x2e\xb3\x2b\xab\xba\x5c\xc2\x6b\xac\xc8\x23\x02\xa3\x7f\x1a\x08\x0f\x52\x6c\x15\xca\x6b\xd0\x72\x0f\x5a\xc0\x86\x6a\x20\x0a\x6a\xc2\xf7\x40\x39\x10\x90\x62\xeb\x48\x30\xd8\x8b\xa3\xc8\xc0\x4c\x8c\x5f\xda\x47\xe2\x7f\xbe\x84\xe0\x59\x18\xf2\x52\x57\xfe\xe8\xfc\xdc\x3f\xae\x4d\xa2\x06\x5d\x21\x70\xdc\x69\xd8\x5a\xb0\x5a\x00\x29\x0a\x73\x3e\x70\xa6\x4b\x43\x72\x8c\x68\xf0\xf2\xc5\x5a\xff\x9a\x11\x99\xc2\x79\x90\x31\xef\xea\x76\x33\xf5\x44\x15\x68\x21\xcc\x3f\x68\x88\xa2\x04\x7b\x44\xab\xe3\xb3\x3b\xbc\x9d\x32\x36\x0c\x43\x47\xcf\x72\x1e\xc4\xf6\x0a\x8e\xab\xcb\xc3\xf4\x38\x4a\xd2\x7c\x14\x9a\xb0\xe0\x35\xe1\xac\xe3\xfc\x62\xfc\xa2\xe7\x70\x69\x49\xdb\xdd\x55\x39\x61\xd8\xe3\x1f\x97\xf0\xa2\xb7\x9c\xc2\x72\x12\x61\x70\xe6\xcc\x05\x36\x4d\xec\x3f\x60\x8a\x75\xb9\x02\x7b\x61\x44\xaa\xe1\x9e\x4b\xad\xc9\xed\x9d\x8d\xaf\x6d\x00\x1f\x51\xee\xa1\x91\xf8\x48\x45\xab\x42\xf2\x47\x9e\x2c\xab\x31\xd2\x97\x8e\x13\x63\x69\xcf\x90\x28\x1a\xea\xc3\x35\x00\x2f\x8e\xaa\xeb\x20\x90\xb9\x17\x6e\x43\xe1\x98\x1b\xa1\x79\x43\x11\x7f\xe5\x90\xda\xbf\x07\x97\x97\xe7\x6b\xce\x16\xfd\x44\xe3\x7c\x3d\xbc\xbf\xeb\x30\xae\xb0\xee\xd0\x24\xb7\x26\x06\x98\xb4\x74\x86\x02\x73\xc3\xa7\x6a\x54\x36\x47\x99\x38\x51\x30\x27\xb3\x72\x3a\x27\xd5\x75\x50\xfc\x0e\xde\x36\x14\xfd\x44\x3e\x6c\x36\x2c\x0c\x89\xba\x95\x3c\xe8\x4e\xdd\x88\x38\x83\x38\x82\x33\x78\x23\x0a\xb4\x3d\x8e\xc0\xa6\xd5\xad\x44\xa0\x75\xc3\xb0\x46\xae\xcd\x64\xe0\xa0\x2b\xa2\x01\x6b\xaa\x95\xe1\x04\xd7\xca\xcc\xb3\xb6\x29\x86\xee\x67\x58\x28\x18\x66\x05\x3e\xb4\x65\x32\xb3\x67\x94\x97\xc0\xec\x98\x9d\x59\xc2\xf6\xcd\xf6\x2e\xaf\xb0\x68\x4d\x87\xe6\xe0\xac\x98\x5e\x5d\x93\x1d\xad\xdb\x1a\x2e\x2f\xde\x7d\x8b\x23\xe5\x75\x3e\x85\x5e\x4c\xd1\x5a\xd8\xdd\x69\xe1\x8e\x7d\x2a\x1d\x4e\x3f\x1f\xa2\x53\x8a\xb0\x06\x85\xfa\x23\xad\x51\xb4\x3a\x49\x52\x58\xbf\x0a\xa6\xce\xb1\x32\x6f\x19\x5b\xf5\xe7\x1d\x62\xeb\x61\x6e\x06\x71\x6a\x33\x69\x06\xfc\x21\x8e\x71\x67\xea\x18\x0a\xdc\x90\x96\x69\x70\x7b\xc6\x5b\xc2\x98\xa9\xa2\xa7\xf1\x02\x80\x0c\x83\x1d\x00\x6d\x3f\x40\x86\xfd\x60\x67\xe1\x7a\xb2\xb6\x83\x2d\xdc\x58\x4c\x0c\xfd\xb6\x20\xc9\xc3\x5b\xd2\xb8\xb0\x7a\xc9\xeb\xbe\xfc\x27\x07\x9f\xc3\x7e\x13\xda\xe9\xe6\x68\xa7\x10\xdc\xeb\x8e\x6e\x39\xd5\x94\xb0\xe0\xa4\x90\x64\x9b\xa4\xc3\xca\x71\xb3\xd3\x92\xe4\xb6\x3f\xc2\x23\x61\xad\x69\xb6\x7d\x80\xdd\xe4\xbd\xb3\xf4\x3a\x22\x99\x80\xb2\x45\xa5\x60\x23\x45\x0d\x0d\x29\x0a\x43\xa0\xce\x88\xef\x73\x8d\x19\x4b\x5b\xca\x0b\xb1\xcd\x4a\xd4\x6f\x44\xdd\xb4\x1a\x8b\x3b\xbd\x67\x98\x74\xb9\x4c\xcd\xd1\x6f\x52\x34\x28\xf5\xfe\xb3\x31\x90\xcc\xbc\xbd\x05\xc3\x8d\x9e\xa5\x99\xc4\x86\x91\xdc\xc8\x77\xb3\x39\xcc\x66\xe9\xc9\xcc\xfb\x4d\xac\x21\x52\xe1\x2d\xd7\x49\xb3\x4b\xe1\x0c\xae\xe0\xfb\x77\x33\xa4\xa7\x98\xc3\x59\x3a\x7d\x82\x60\x6d\xfa\xc7\x10\x16\x8b\xd2\x91\x69\x61\x3c\x2d\x5c\xff\xf9\x79\x2c\xa3\x25\x72\x04\xe9\x34\x9c\x6e\x30\xb9\xf9\xea\x20\x4d\xc8\x33\x30\x78\xe4\x69\xba\x95\x7a\xb0\x94\x73\x94\x5d\x83\xf5\xce\x6e\xf9\x46\xc8\x1a\x44\x2b\x01\x1d\xd9\xc5\xe6\xc8\xf3\x31\x33\xad\x63\x6f\x96\x14\xc5\x8d\x69\x4a\xef\x8d\x0e\x47\x99\xcc\x24\x2a\xfa\x0d\x67\x73\x08\x0a\xbc\x6f\x1d\x3f\x8a\xf4\x44\xa8\xdd\xec\x1a\x7a\x8b\x9b\x2f\xcf\x16\xda\x0f\x2a\xcd\x8e\xd7\xe3\x20\xa6\xad\x6e\xb0\xd2\x57\x57\x74\x48\x4f\xac\xf5\xd4\x95\xa3\x69\x1c\xb5\xeb\xca\x47\x85\x1a\xf0\xce\x93\xe7\x56\x63\x7d\xc3\x50\x75\x33\x11\x19\x66\x7f\xb5\x28\xf7\x77\xc8\xd0\xf4\xa6\xff\x33\x96\xcc\xb2\x8e\x6a\x54\x63\xed\x18\x65\xc7\x9c\x33\x65\xfa\x95\xd8\x4c\x2d\xfa\x54\x39\x15\x22\x5d\x47\xcb\x0a\xa2\x89\x42\x9d\x11\x39\xe0\x1a\xc1\xef\xbf\x33\x26\x9b\xfd\x9b\x6e\xc1\x0e\xd7\x6a\xc2\x0b\x30\x59\x31\x9b\x5d\x1d\x47\x2e\x41\x01\xc8\xd1\x06\x7e\xec\x2b\x58\xda\x57\xff\x46\x62\xbe\xf8\x6f\x15\x64\xf8\xf5\x44\x86\x32\xe4\x5a\x52\x54\x49\x3a\xca\x55\x1f\x34\xac\x03\x00\x7e\x03\x5e\x75\xec\xfd\x4f\x7f\x14\xae\xa2\x66\xe8\xa2\x94\x42\x26\xf7\xbf\x52\xa5\x4c\xcb\x9c\x7e\xb8\x38\x76\x98\x11\xfb\xe2\xc9\xda\x3c\xdc\xfb\x05\x72\x4a\xea\x00\x09\xad\xcb\x1b\xf7\xca\xa7\xf3\x40\xeb\x72\xd6\xa1\x88\x9c\x72\xa6\x4c\x0b\xcb\xaa\xae\xc7\xdc\xbf\x78\xea\x43\xc9\xaa\x43\xb3\xbb\x5f\x1d\x6b\x6f\x7d\x8f\x18\x29\x6f\x7b\xe5\x43\x47\x89\x43\xfc\x77\x00\x00\x00\xff\xff\x24\xc0\x4e\x7e\xb8\x0f\x00\x00") + +func sharedJsGalleryJsBytes() ([]byte, error) { + return bindataRead( + _sharedJsGalleryJs, + "shared/js/gallery.js", + ) +} + +func sharedJsGalleryJs() (*asset, error) { + bytes, err := sharedJsGalleryJsBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "shared/js/gallery.js", size: 4024, mode: os.FileMode(420), modTime: time.Unix(1590429739, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + +var _sharedJsViewJs = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x94\x52\xcf\x4e\xf3\x30\x0c\x3f\x7f\x79\x0a\xdf\x9a\x4a\xdf\xb2\x07\x88\x76\x00\x31\xe0\x88\xc4\x11\x71\xc8\x5a\xb7\x2b\x72\xe3\xe1\x64\xaa\x26\xc4\xbb\xa3\x52\x77\x1b\x68\x3b\x70\x8c\x7f\x7f\xed\xb6\xeb\x77\x2c\x19\x1e\x02\x11\xca\x01\x1a\xe1\x1e\x0a\xb7\x6c\xa7\xb7\x7b\x4b\x85\x37\xca\x59\x47\x0a\xd2\xe2\x91\x83\xd3\x7b\xe2\x98\xe5\x12\x6e\xbb\x58\x03\x12\xf6\x18\x73\x32\xa6\xe2\x98\x32\xa8\xd3\x9a\x10\x56\x50\x73\xb5\x1f\x51\xf7\xbe\x47\x39\x3c\x23\x61\x95\x59\x6c\xd1\x87\x2e\x3a\x65\x16\xa5\xff\x29\x85\x15\x44\x1c\xe6\x8a\xf6\x64\x58\xfa\x39\x44\xab\xac\x09\xd3\xd5\x94\x1b\x22\x5b\xbc\xd4\x21\x87\x85\xd2\x5f\x4f\x51\x3a\xd1\x28\xdd\xd4\x1e\x8d\x36\x5c\x1f\x4a\x6f\x1a\x16\xb0\x2a\x20\x04\x6e\xce\x83\x4b\xf8\x30\xff\xe6\x9b\x08\xb6\x5d\xca\x28\x16\x09\xdd\x98\x99\x30\x3b\x05\xff\x8f\xe2\xd2\x9b\xcf\x5f\xed\x9f\x04\x89\x43\xfd\xc7\x25\x16\xbb\x49\xb6\x68\x58\xbe\x17\xba\x5a\xf2\xcc\xff\x62\x57\xc5\x2f\x55\x56\xe8\x9e\xe5\xac\xfc\xac\x1f\x42\xae\xb6\x8f\x21\x6d\x6d\x39\xfd\x07\x77\xb8\xd9\xb7\xc6\x0c\x5d\xac\x79\x70\x2d\xac\xe6\x2f\xe9\xe7\xd9\x78\x68\x95\x7b\xf3\x15\x00\x00\xff\xff\xe9\xb5\xb6\x88\x83\x02\x00\x00") + +func sharedJsViewJsBytes() ([]byte, error) { + return bindataRead( + _sharedJsViewJs, + "shared/js/view.js", + ) +} + +func sharedJsViewJs() (*asset, error) { + bytes, err := sharedJsViewJsBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "shared/js/view.js", size: 643, mode: os.FileMode(420), modTime: time.Unix(1590429739, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + +var _viewIndexTmpl = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xb4\x56\x4d\x6f\xdb\x38\x10\x3d\xdb\xbf\x62\xc2\xbd\xec\x02\x91\x64\x27\x41\x76\x37\xa0\x04\x14\x4d\x7b\x28\x10\x34\x40\xdd\x43\x8f\x63\x69\x22\x31\xa1\x28\x81\xa4\xbf\xea\xea\xbf\x17\x24\x65\xc7\x71\x9d\xc0\x29\xd2\x93\xcc\xe1\xcc\xbc\x99\xc7\xc7\xa1\xf9\xc9\xf5\xe7\xf7\x93\x6f\xb7\x1f\xa0\xb2\xb5\xcc\x86\xdc\x7d\x40\xa2\x2a\x53\x46\x8a\x39\x03\x61\x91\x0d\x07\xbc\x26\x8b\x90\x57\xa8\x0d\xd9\x94\x7d\x9d\x7c\x8c\xfe\x63\x5b\xbb\xc2\x9a\x52\x36\x17\xb4\x68\x1b\x6d\x19\xe4\x8d\xb2\xa4\x6c\xca\x16\xa2\xb0\x55\x5a\xd0\x5c\xe4\x14\xf9\xc5\x29\x08\x25\xac\x40\x19\x99\x1c\x25\xa5\xe3\x78\xe4\xf3\x58\x61\x25\x65\xeb\x35\xc4\x37\x64\xb1\x40\x8b\xf1\xc4\x99\xa0\xeb\x78\x12\x36\x87\x03\x2e\x85\x7a\x00\x4d\x32\x65\xc6\xae\x24\x99\x8a\xc8\x32\xa8\x34\xdd\xa5\xcc\xc5\xbe\x33\x86\xac\x81\xae\xcb\x8d\x49\xa6\x68\x28\xce\x8d\x61\xaf\x8e\x2c\x51\x4a\xd2\xab\xdf\x0b\x26\x25\x51\x97\x1b\x64\x9e\x04\x06\xf9\xb4\x29\x56\x2e\x97\xc2\x79\x36\x1c\x0c\xf8\x4c\xba\x8f\xcb\x9d\x71\xec\x73\xfd\x55\x34\x0b\x25\x1b\x2c\x58\x76\xdd\xff\xe2\x09\x66\x3c\x91\xe2\x57\xe7\x1a\x15\x96\xc4\xb2\x1b\xff\xdd\xf5\xe3\x89\x4f\xce\x93\x80\xe5\xcf\x90\xb4\xdf\xa9\xc6\xcf\x71\x5c\x8d\x7d\xc8\xd6\x97\xd7\x28\x14\xe4\x12\x8d\x49\x59\x4f\x88\x23\x63\xb0\x5e\x83\x46\x55\x12\xc4\xb7\x55\x63\x1b\xd7\xb6\xcb\x5c\x88\xf9\x9e\x77\x24\x2c\xd5\xd0\x6a\x72\x7d\x50\x11\xd9\x6a\x56\x4f\x15\x0a\xc9\xc0\x61\x47\xa8\x3d\x7b\xa8\x21\x86\xae\x63\xa1\x43\xdc\xa1\xd5\x59\x83\x6b\xcf\xe9\xa3\x39\x38\xb7\x22\xb7\x33\x4d\x7e\x31\xe0\xa6\x99\xe9\x9c\xc0\xe8\xdc\x6b\x74\xbd\x76\xd8\x4e\x94\x10\xc3\xf9\xd9\x08\x98\xa8\xb1\xa4\x64\x41\xd3\x96\xc1\xbf\x23\xe8\x3a\x38\x5b\x9e\xc2\x13\xbf\xf1\xe5\x41\xbf\xf1\x92\x81\x5d\xb5\x94\x3e\xd9\xab\xa9\x10\x98\xb2\xbf\x6b\x5c\x06\x71\x5f\xc1\xff\xa3\x51\xbb\xfc\x87\x1d\x51\xd1\xe5\xc5\x71\x15\x3d\x53\xf9\xc1\x8a\x5e\x47\xc4\x7d\x4b\xe5\x31\x44\xec\xfa\xed\xc3\x86\xbd\xb7\x22\xe2\xc5\x8a\x9e\xa9\xfc\x60\x45\x3d\xac\xa8\x4b\x87\x79\x14\x05\x0c\x7c\xcc\x00\xa5\x4d\xd9\x66\x11\xe6\xd7\x56\xa5\x3f\xa0\x9e\x49\x1f\xdf\x75\x2c\x78\x54\x24\xca\xca\xa6\xec\xfc\x6c\xd4\x5b\x9c\xda\x85\x1b\xa0\x12\xbf\xaf\x7a\xdb\xae\x88\xa3\xfe\x46\x44\x77\x8d\xde\x17\x74\xb2\xab\xe8\x8d\xbc\x37\xd7\xea\xc0\x4d\x8a\x7c\x1b\x47\x9d\xfb\x9e\x8a\xce\x2f\xfe\x94\xae\x8f\x07\x7a\x55\xd9\xe1\xa8\x0e\x67\x7b\x0b\x15\x1e\x0f\xf4\xb2\xb8\x0e\xe5\x79\x03\x6d\x1d\xd2\x47\x82\x61\xda\x17\x62\xde\x0f\x66\x52\x85\x1f\xc7\x3c\x71\xd3\xdb\x4d\xf1\x93\x28\x82\x49\x25\x0c\xf4\x43\x19\x16\x8d\x7e\x30\xb0\x10\xb6\x6a\x66\x16\x3e\xe1\x1c\xbf\xe4\x5a\xb4\xf6\x04\xa2\xc8\x05\x18\xbf\xea\xbb\xae\x9b\x62\x26\x89\x6d\xfb\x7c\x7c\xe9\xee\x4d\xe2\x3a\x8e\xef\x0d\x03\x34\x2b\x95\x67\x3c\x09\xa1\xee\xcd\x0b\x8f\x1d\x4f\xfa\x7f\x15\xae\x88\xb9\xa8\xaf\xc0\x90\x85\x3b\x9b\x3a\xf3\x95\xc7\xfb\x19\x00\x00\xff\xff\x7e\x6f\x73\xd7\x81\x08\x00\x00") + +func viewIndexTmplBytes() ([]byte, error) { + return bindataRead( + _viewIndexTmpl, + "view/index.tmpl", + ) +} + +func viewIndexTmpl() (*asset, error) { + bytes, err := viewIndexTmplBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "view/index.tmpl", size: 2177, mode: os.FileMode(420), modTime: time.Unix(1590932953, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + +// Asset loads and returns the asset for the given name. +// It returns an error if the asset could not be found or +// could not be loaded. +func Asset(name string) ([]byte, error) { + cannonicalName := strings.Replace(name, "\\", "/", -1) + if f, ok := _bindata[cannonicalName]; ok { + a, err := f() + if err != nil { + return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err) + } + return a.bytes, nil + } + return nil, fmt.Errorf("Asset %s not found", name) +} + +// MustAsset is like Asset but panics when Asset would return an error. +// It simplifies safe initialization of global variables. +func MustAsset(name string) []byte { + a, err := Asset(name) + if err != nil { + panic("asset: Asset(" + name + "): " + err.Error()) + } + + return a +} + +// AssetInfo loads and returns the asset info for the given name. +// It returns an error if the asset could not be found or +// could not be loaded. +func AssetInfo(name string) (os.FileInfo, error) { + cannonicalName := strings.Replace(name, "\\", "/", -1) + if f, ok := _bindata[cannonicalName]; ok { + a, err := f() + if err != nil { + return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err) + } + return a.info, nil + } + return nil, fmt.Errorf("AssetInfo %s not found", name) +} + +// AssetNames returns the names of the assets. +func AssetNames() []string { + names := make([]string, 0, len(_bindata)) + for name := range _bindata { + names = append(names, name) + } + return names +} + +// _bindata is a table, holding each asset generator, mapped to its name. +var _bindata = map[string]func() (*asset, error){ + "shared/css/base.css": sharedCssBaseCss, + "shared/css/enlarge.css": sharedCssEnlargeCss, + "shared/css/gallery.css": sharedCssGalleryCss, + "shared/js/bleh.js": sharedJsBlehJs, + "shared/js/enlarge.js": sharedJsEnlargeJs, + "shared/js/gallery.js": sharedJsGalleryJs, + "shared/js/view.js": sharedJsViewJs, + "view/index.tmpl": viewIndexTmpl, +} + +// AssetDir returns the file names below a certain +// directory embedded in the file by go-bindata. +// For example if you run go-bindata on data/... and data contains the +// following hierarchy: +// data/ +// foo.txt +// img/ +// a.png +// b.png +// then AssetDir("data") would return []string{"foo.txt", "img"} +// AssetDir("data/img") would return []string{"a.png", "b.png"} +// AssetDir("foo.txt") and AssetDir("notexist") would return an error +// AssetDir("") will return []string{"data"}. +func AssetDir(name string) ([]string, error) { + node := _bintree + if len(name) != 0 { + cannonicalName := strings.Replace(name, "\\", "/", -1) + pathList := strings.Split(cannonicalName, "/") + for _, p := range pathList { + node = node.Children[p] + if node == nil { + return nil, fmt.Errorf("Asset %s not found", name) + } + } + } + if node.Func != nil { + return nil, fmt.Errorf("Asset %s not found", name) + } + rv := make([]string, 0, len(node.Children)) + for childName := range node.Children { + rv = append(rv, childName) + } + return rv, nil +} + +type bintree struct { + Func func() (*asset, error) + Children map[string]*bintree +} + +var _bintree = &bintree{nil, map[string]*bintree{ + "shared": &bintree{nil, map[string]*bintree{ + "css": &bintree{nil, map[string]*bintree{ + "base.css": &bintree{sharedCssBaseCss, map[string]*bintree{}}, + "enlarge.css": &bintree{sharedCssEnlargeCss, map[string]*bintree{}}, + "gallery.css": &bintree{sharedCssGalleryCss, map[string]*bintree{}}, + }}, + "js": &bintree{nil, map[string]*bintree{ + "bleh.js": &bintree{sharedJsBlehJs, map[string]*bintree{}}, + "enlarge.js": &bintree{sharedJsEnlargeJs, map[string]*bintree{}}, + "gallery.js": &bintree{sharedJsGalleryJs, map[string]*bintree{}}, + "view.js": &bintree{sharedJsViewJs, map[string]*bintree{}}, + }}, + }}, + "view": &bintree{nil, map[string]*bintree{ + "index.tmpl": &bintree{viewIndexTmpl, map[string]*bintree{}}, + }}, +}} + +// RestoreAsset restores an asset under the given directory +func RestoreAsset(dir, name string) error { + data, err := Asset(name) + if err != nil { + return err + } + info, err := AssetInfo(name) + if err != nil { + return err + } + err = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755)) + if err != nil { + return err + } + err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode()) + if err != nil { + return err + } + err = os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime()) + if err != nil { + return err + } + return nil +} + +// RestoreAssets restores an asset under the given directory recursively +func RestoreAssets(dir, name string) error { + children, err := AssetDir(name) + // File + if err != nil { + return RestoreAsset(dir, name) + } + // Dir + for _, child := range children { + err = RestoreAssets(dir, filepath.Join(name, child)) + if err != nil { + return err + } + } + return nil +} + +func _filePath(dir, name string) string { + cannonicalName := strings.Replace(name, "\\", "/", -1) + return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...) +} diff --git a/cmd/web/templates.go b/cmd/web/templates.go new file mode 100644 index 0000000..581ff76 --- /dev/null +++ b/cmd/web/templates.go @@ -0,0 +1,45 @@ +package main + +import ( + "html/template" + + lib "git.makerforce.io/photos/photos/pkg/bucket" +) + +var funcs = template.FuncMap{ + "ar": func(p Photo) float64 { return float64(p.Width) / float64(p.Height) }, + "mul": func(a, b float64) float64 { return a * b }, + "preview": preview, +} + +type IndexTemplateData struct { + Assets string + Metadata lib.BucketMetadata + Photos []Photo +} + +type Photo struct { + lib.Photo + lib.PhotoMetadata +} + +var indexTemplate = mustTemplateAsset("index") + +func mustTemplateAsset(name string) *template.Template { + buf, err := Asset("view/" + name + ".tmpl") + if err != nil { + panic(err) + } + return template.Must( + template.New(name).Funcs(funcs).Parse(string(buf)), + ) +} + +func preview(p Photo, height int, format lib.PhotoFormat, quality int) template.Srcset { + preview := p.GetPreview(lib.PreviewOption{ + Height: height, + Format: format, + Quality: quality, + }) + return template.Srcset(preview.String()) +} diff --git a/cmd/web/web.go b/cmd/web/web.go new file mode 100644 index 0000000..cfecb6c --- /dev/null +++ b/cmd/web/web.go @@ -0,0 +1,92 @@ +package main + +//go:generate go-bindata -fs -prefix "../../web/" "../../web/..." + +import ( + "bytes" + "fmt" + "net/http" + "os" + "time" + + "git.makerforce.io/photos/photos/internal/httphelpers" + lib "git.makerforce.io/photos/photos/pkg/bucket" +) + +var client *lib.Client + +var endpoint string + +func main() { + // Read configuration + endpoint = os.Getenv("WEB_ENDPOINT") + + // Setup bucket client + var err error + client, err = lib.NewClientFromEnv() + if err != nil { + panic(err) + } + + server := &http.Server{ + Addr: ":8004", + ReadTimeout: 5 * time.Second, + WriteTimeout: 10 * time.Second, + } + http.Handle("/shared/", http.FileServer(AssetFile())) + http.HandleFunc("/update", update) + err = server.ListenAndServe() + if err != nil { + panic(err) + } +} + +func update(w http.ResponseWriter, req *http.Request) { + if req.Method != http.MethodPost { + err := fmt.Errorf("%w: %v", httphelpers.ErrorMethodNotAllowed, req.Method) + httphelpers.ErrorResponse(w, err) + return + } + + bucket := lib.Bucket(req.FormValue("bucket")) + /* + bucketMetadata, err := client.GetBucketMetadata(bucket) + previewOptions := bucketMetadata.PreviewOptions + */ + + metadata, err := client.GetBucketMetadata(bucket) + photos, err := client.GetPhotos(bucket) + if err != nil { + httphelpers.ErrorResponse(w, err) + return + } + + detailedPhotos := make([]Photo, 0) + for _, photo := range photos { + photoMetadata, err := client.GetPhotoMetadata(bucket, photo) + if err != nil { + err := fmt.Errorf("%w: %v", err, photo) + httphelpers.ErrorResponse(w, err) + return + } + detailedPhotos = append(detailedPhotos, Photo{photo, photoMetadata}) + } + + data := IndexTemplateData{ + Assets: endpoint + "/shared/", + Metadata: metadata, + Photos: detailedPhotos, + } + indexBuf := new(bytes.Buffer) + err = indexTemplate.Execute(indexBuf, data) + if err != nil { + httphelpers.ErrorResponse(w, err) + return + } + + err = client.PutBucketWeb(bucket, indexBuf.Bytes()) + if err != nil { + httphelpers.ErrorResponse(w, err) + return + } +} diff --git a/go.mod b/go.mod index aaff3f3..4e04815 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,11 @@ go 1.14 require ( github.com/davidbyttow/govips v0.0.0-20200412130214-cbefdd8c639a + github.com/elazarl/go-bindata-assetfs v1.0.0 + github.com/go-bindata/go-bindata v1.0.0 // indirect + github.com/go-bindata/go-bindata/v3 v3.1.3 // indirect github.com/miekg/dns v1.1.29 github.com/minio/minio-go/v6 v6.0.55 + golang.org/x/lint v0.0.0-20200302205851-738671d3881b // indirect + golang.org/x/tools v0.0.0-20200530233709-52effbd89c51 // indirect ) diff --git a/go.sum b/go.sum index 6d7c0db..f5abce1 100644 --- a/go.sum +++ b/go.sum @@ -4,6 +4,12 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/davidbyttow/govips v0.0.0-20200412130214-cbefdd8c639a h1:g1X1c43wACmPtvFo+szBy/0coLkRwVJ0+i2f6YJITto= github.com/davidbyttow/govips v0.0.0-20200412130214-cbefdd8c639a/go.mod h1:a3qO525EPfJNYa0NXBcNtXzJvyQsJAxphEDa7OOHPBk= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/elazarl/go-bindata-assetfs v1.0.0 h1:G/bYguwHIzWq9ZoyUQqrjTmJbbYn3j3CKKpKinvZLFk= +github.com/elazarl/go-bindata-assetfs v1.0.0/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4= +github.com/go-bindata/go-bindata v1.0.0 h1:DZ34txDXWn1DyWa+vQf7V9ANc2ILTtrEjtlsdJRF26M= +github.com/go-bindata/go-bindata v1.0.0/go.mod h1:xK8Dsgwmeed+BBsSy2XTopBn/8uK2HWuGSnA11C3Joo= +github.com/go-bindata/go-bindata/v3 v3.1.3 h1:F0nVttLC3ws0ojc7p60veTurcOm//D4QBODNM7EGrCI= +github.com/go-bindata/go-bindata/v3 v3.1.3/go.mod h1:1/zrpXsLD8YDIbhZRqXzm1Ghc7NhEvIN9+Z6R5/xH4I= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= @@ -11,6 +17,8 @@ github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGn github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/kisielk/errcheck v1.2.0 h1:reN85Pxc5larApoH1keMBiu2GWtPqXQ1nc9gx+jOU+E= +github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/miekg/dns v1.1.29 h1:xHBEhR+t5RzcFJjBLJlax2daXOrTYtr9z4WdKEfWFzg= github.com/miekg/dns v1.1.29/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= @@ -35,12 +43,18 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f h1:R423Cnkcp5JABoeemiGEPlt9tHXFfw5kvc0yqlxRPWo= golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f h1:J5lckAjkw6qYlOZNj90mLYNTEKDvWeuc1yieZ8qUzUE= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b h1:Wh+f8QHJXR411sJR8/vRBTZ7YapZaRvUcLFFJhusH0k= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190522155817-f3200d17e092 h1:4QSRKanuywn15aTZvI/mIDEgPQpswuFndXpOj3rKEco= @@ -48,7 +62,10 @@ golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190923162816-aa69164e4478 h1:l5EDrHhldLYb3ZRHDUhXF7Om7MvYXnkV9/iQNo1lX6g= golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b h1:0mm1VjtFUOIlE1SbDlwjYaDxZVDP2S5ou6y0gSgXHu8= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/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-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc= @@ -57,8 +74,18 @@ golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe h1:6fAMxZRR6sl1Uq8U61gxU+kPT golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216052735-49a3e744a425 h1:VvQyQJN0tSuecqgcIxMWnnfG5kSmgy9KZR9sW3W5QeA= golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200530233709-52effbd89c51 h1:Wec8/IO8hAraBf0it7/dPQYOslIrgM938wZYNkLnOYc= +golang.org/x/tools v0.0.0-20200530233709-52effbd89c51/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +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= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/ini.v1 v1.42.0 h1:7N3gPTt50s8GuLortA00n8AqRTk75qOP98+mTPpgzRk= gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= diff --git a/pkg/bucket/client.go b/pkg/bucket/client.go index 2ad4318..4904e2d 100644 --- a/pkg/bucket/client.go +++ b/pkg/bucket/client.go @@ -78,6 +78,42 @@ func (c *Client) PutBucketMetadata(b Bucket, m BucketMetadata) error { return nil } +func (c *Client) PutBucketWeb(b Bucket, buf []byte) error { + err := b.Validate() + if err != nil { + return err + } + + _, err = c.PutObject(b.String(), "index.html", bytes.NewBuffer(buf), int64(len(buf)), minio.PutObjectOptions{ + ContentType: "text/html", + StorageClass: "REDUCED_REDUNDANCY", + }) + if err != nil { + return err + } + return nil +} + +func (c *Client) GetPhotos(b Bucket) ([]Photo, error) { + err := b.Validate() + if err != nil { + return nil, err + } + + keys := make([]Photo, 0) + + doneCh := make(chan struct{}) + objCh := c.ListObjectsV2(b.String(), photoPrefix, true, doneCh) + for obj := range objCh { + if obj.Err != nil { + return nil, obj.Err + } + keys = append(keys, Photo(obj.Key)) + } + + return keys, nil +} + func (c *Client) GetPhoto(b Bucket, p Photo) ([]byte, error) { err := b.Validate() if err != nil { @@ -96,6 +132,58 @@ func (c *Client) GetPhoto(b Bucket, p Photo) ([]byte, error) { return ioutil.ReadAll(o) } +func (c *Client) GetPhotoMetadata(b Bucket, p Photo) (PhotoMetadata, error) { + err := b.Validate() + if err != nil { + return PhotoMetadata{}, err + } + err = p.Validate() + if err != nil { + return PhotoMetadata{}, err + } + + o, err := c.GetObject(b.String(), p.MetadataString(), minio.GetObjectOptions{}) + if err != nil { + return PhotoMetadata{}, err + } + defer o.Close() + buf, err := ioutil.ReadAll(o) + if err != nil { + return PhotoMetadata{}, err + } + + m := PhotoMetadata{} + err = json.Unmarshal(buf, &m) + if err != nil { + return PhotoMetadata{}, err + } + return m, nil +} + +func (c *Client) PutPhotoMetadata(b Bucket, p Photo, m PhotoMetadata) error { + err := b.Validate() + if err != nil { + return err + } + err = p.Validate() + if err != nil { + return err + } + + buf, err := json.MarshalIndent(m, "", "\t") + if err != nil { + return err + } + + _, err = c.PutObject(b.String(), p.MetadataString(), bytes.NewBuffer(buf), int64(len(buf)), minio.PutObjectOptions{ + ContentType: "application/json", + }) + if err != nil { + return err + } + return nil +} + func (c *Client) PutPreview(b Bucket, p Preview, buf []byte) error { err := b.Validate() if err != nil { diff --git a/pkg/bucket/photo.go b/pkg/bucket/photo.go index a5c7bdb..f028b4a 100644 --- a/pkg/bucket/photo.go +++ b/pkg/bucket/photo.go @@ -26,3 +26,17 @@ func (p Photo) String() string { func (p Photo) Path() string { return "/" + string(p) } + +const photoMetadataPrefix = "photometa/" + +func (p Photo) MetadataString() string { + objectBase := strings.Replace(string(p), photoPrefix, photoMetadataPrefix, 1) + objectBase += ".metadata.json" + return objectBase +} + +type PhotoMetadata struct { + Title string `json:"title"` + Width int `json:"width"` + Height int `json:"height"` +} diff --git a/pkg/bucket/photopreview.go b/pkg/bucket/photopreview.go index 886eb3b..9b08c24 100644 --- a/pkg/bucket/photopreview.go +++ b/pkg/bucket/photopreview.go @@ -95,7 +95,7 @@ func (p Photo) GetPreview(option PreviewOption) Preview { } var defaultSizes = []int{640, 320, 160, 80} -var defaultSizesThumb = []int{120, 60, 30} +var defaultSizesThumb = []int{60, 30} var defaultFormats = []PhotoFormat{ PhotoFormatWEBP, PhotoFormatJPEG, diff --git a/web/Caddyfile b/web/Caddyfile deleted file mode 100644 index 180efe3..0000000 --- a/web/Caddyfile +++ /dev/null @@ -1,12 +0,0 @@ -local1:2020 { - root * ./shared - file_server browse - header Access-Control-Allow-Origin "*" - tls internal -} - -local2:2020 { - root * ./view - file_server browse - tls internal -} diff --git a/web/view/index.tmpl b/web/view/index.tmpl index e77e006..2defd90 100644 --- a/web/view/index.tmpl +++ b/web/view/index.tmpl @@ -3,10 +3,10 @@ - Photos - - - + {{ .Metadata.Title }} + + +
-

James Birthday 2020

+

{{ .Metadata.Title }}

- {{ range (datasource "samples") }} -
- + diff --git a/web/view/sample.sh b/web/view/sample.sh deleted file mode 100755 index 815d27d..0000000 --- a/web/view/sample.sh +++ /dev/null @@ -1,34 +0,0 @@ -#!/bin/sh - -set -e - -sizes=(640 320 160 80) -presizes=(120 60 30) - -rm samples.csv - -echo "Getting file dimensions..." -for f in sample/*unsplash.jpg; do - gm identify -format %f,%w,%h $f >> samples.csv -done - -for size in ${presizes[*]}; do - for f in sample/*unsplash.jpg; do - echo "Compressing thumbnail $f at $size" - if [ -f "${f%.jpg}_pre$size.jpg" ]; then - continue - fi - gm convert $f -resize x$size -compress jpeg -quality 40 ${f%.jpg}_pre$size.jpg - gm convert $f -resize x$size -compress webp -quality 35 ${f%.jpg}_pre$size.webp - done -done -for size in ${sizes[*]}; do - for f in sample/*unsplash.jpg; do - echo "Compressing $f at $size" - if [ -f "${f%.jpg}_$size.jpg" ]; then - continue - fi - gm convert $f -resize x$size -compress jpeg -quality 75 ${f%.jpg}_$size.jpg - gm convert $f -resize x$size -compress webp -quality 70 ${f%.jpg}_$size.webp - done -done diff --git a/web/view/tmpl.sh b/web/view/tmpl.sh deleted file mode 100755 index a221db9..0000000 --- a/web/view/tmpl.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/sh - -set -e - -while true; do - gotemplate -f index.tmpl -d samples.csv -o index.html 2>&1 > /dev/null - date - sleep 5 -done -